From: adam
Date: Fri, 7 May 2004 23:19:36 +0000 (+0000)
Subject: outgoing smtp
X-Git-Url: http://git.megacz.com/?a=commitdiff_plain;h=4a84e96e64310fdfd9d24c691d12586b758a31de;p=org.ibex.mail.git
outgoing smtp
darcs-hash:20040507231936-5007d-c25d4976a117dc406469b4e4b91ed5b42c3edc3d.gz
---
diff --git a/mail.jar b/mail.jar
new file mode 100644
index 0000000..e5ad502
Binary files /dev/null and b/mail.jar differ
diff --git a/src/org/ibex/mail/filter/Filter.java b/src/org/ibex/mail/Filter.java
similarity index 76%
rename from src/org/ibex/mail/filter/Filter.java
rename to src/org/ibex/mail/Filter.java
index ed6aaa0..1b8364b 100644
--- a/src/org/ibex/mail/filter/Filter.java
+++ b/src/org/ibex/mail/Filter.java
@@ -1,5 +1,4 @@
-package org.ibex.mail.filter;
-import org.ibex.mail.*;
+package org.ibex.mail;
/** generic superclass for processing elements that recieve a message and "usually" pass it on */
public class Filter {
public Message process(Message m) { return null; }
diff --git a/src/org/ibex/mail/Message.java b/src/org/ibex/mail/Message.java
index 8303ea8..35840bf 100644
--- a/src/org/ibex/mail/Message.java
+++ b/src/org/ibex/mail/Message.java
@@ -7,13 +7,8 @@ import java.util.*;
import java.net.*;
import java.io.*;
-// NOTE: always use Win32 line endings
-// hard line limit: 998 chars
-// soft line limit (suggested): 78 chars
-// header fields: ascii 33-126 (but no colon)
-// field body: anything ASCII except CRLF
+// soft line limit (suggested): 78 chars / hard line limit: 998 chars
// folded headers: can insert CRLF anywhere that whitespace appears (before the whitespace)
-// body needs CRLF; one or the other alone is not acceptable
// date/time parsing: see 3.3
// FEATURE: MIME RFC2045, 2046, 2049
@@ -42,6 +37,8 @@ public class Message extends JSReflection {
public final Address envelopeFrom;
public final Address[] envelopeTo;
+ public Date arrival = null; // when the message first arrived at this machine
+
public void dump(OutputStream os) throws IOException {
Writer w = new OutputStreamWriter(os);
w.write(allHeaders);
@@ -124,6 +121,9 @@ public class Message extends JSReflection {
}
if (s.indexOf(':') == -1) throw new Malformed("Header line does not contain colon: " + s);
key = s.substring(0, s.indexOf(':'));
+ for(int i=0; i 126)
+ throw new Malformed("Header key contains invalid character \"" + s.charAt(i) + "\"");
String val = s.substring(0, s.indexOf(':') + 1);
while(Character.isSpace(val.charAt(0))) val = val.substring(1);
@@ -167,4 +167,18 @@ public class Message extends JSReflection {
}
private static final Random random = new Random();
+
+ public String summary() {
+ return
+ " Subject: " + m.subject + "\n" +
+ " EnvelopeFrom: " + m.envelopeFrom + "\n" +
+ " EnvelopeTo: " + m.envelopeTo + "\n" +
+ " MessageId: " + m.messageid;
+ }
+
+ public Message bounce(String reason) {
+ // use null-sender for error messages (don't send errors to the null addr)
+ // FIXME
+ return null;
+ }
}
diff --git a/src/org/ibex/mail/target/Target.java b/src/org/ibex/mail/Target.java
similarity index 82%
rename from src/org/ibex/mail/target/Target.java
rename to src/org/ibex/mail/Target.java
index b4425a9..0fea645 100644
--- a/src/org/ibex/mail/target/Target.java
+++ b/src/org/ibex/mail/Target.java
@@ -1,5 +1,4 @@
-package org.ibex.mail.target;
-import org.ibex.mail.*;
+package org.ibex.mail;
import java.io.*;
import org.ibex.js.*;
diff --git a/src/org/ibex/mail/protocol/Incoming.java b/src/org/ibex/mail/protocol/Incoming.java
index d46a75e..2fb68eb 100644
--- a/src/org/ibex/mail/protocol/Incoming.java
+++ b/src/org/ibex/mail/protocol/Incoming.java
@@ -1,12 +1,11 @@
package org.ibex.mail.protocol;
import org.ibex.mail.*;
-import org.ibex.mail.store.*;
import org.ibex.mail.target.*;
import java.io.*;
public class Incoming {
protected void accept(Message m) throws IOException, MailException {
- MessageStore.transcript.add(m); // currently, we write all inbound messages to the transcript
+ FileSystem.transcript.add(m); // currently, we write all inbound messages to the transcript
Target.root.accept(m);
}
}
diff --git a/src/org/ibex/mail/protocol/SMTP.java b/src/org/ibex/mail/protocol/SMTP.java
index ff1a5d7..f996c09 100644
--- a/src/org/ibex/mail/protocol/SMTP.java
+++ b/src/org/ibex/mail/protocol/SMTP.java
@@ -1,15 +1,17 @@
package org.ibex.mail.protocol;
import org.ibex.mail.*;
-import org.ibex.mail.store.*;
import org.ibex.mail.target.*;
import org.ibex.util.*;
import java.net.*;
import java.io.*;
import java.util.*;
import java.text.*;
+import javax.naming.*;
+import javax.naming.directory.*;
public class SMTP extends MessageProtocol {
+ static { new Thread() { public void run() { Outgoing.runq(); } }.start(); }
public static String convdir = null;
public static void main(String[] s) throws Exception {
String logto = System.getProperty("ibex.mail.root", File.separatorChar + "var" + File.separatorChar + "org.ibex.mail");
@@ -34,29 +36,92 @@ public class SMTP extends MessageProtocol {
public void handle(Socket s) { new Listener(s, "megacz.com").handleRequest(); }
+ // FEATURE: exponential backoff on retry time?
public static class Outgoing {
- // recommended retry interval is 30 minutes
- // give up after 4-5 days
- // should keep per-host success/failure so we don't retry on every message
- // exponential backoff on retry time?
- // check DNS resolvability as soon as domain is provided
- // only use implicit A-record if there are no MX-records
- // use null-sender for error messages (don't send errors to the null addr)
- // to prevent mail loops, drop messages with >100 Recieved headers
- private final org.ibex.util.Queue queue = new org.ibex.util.Queue(100);
- public static void send(Message m) { }
- public static void enqueue(Message m) { }
- public static void bounce(Message m, String reason) { }
- private void runq() {
- /*
- MessageStore store = MessageStore.root.slash("smtp").slash("outgoing");
+ private static final HashSet deadHosts = new HashSet();
+ private static final org.ibex.util.Queue queue = new org.ibex.util.Queue(100);
+ private static FileSystem store = FileSystem.root.slash("outgoing");
+
+ public static void send(Message m) {
+ if (m.traces.length >= 100) {
+ Log.warn("Message with " + m.traces.length + " trace hops; silently dropping\n" + m.summary());
+ return;
+ }
+ synchronized(Outgoing.class) {
+ store.add(m);
+ queue.append(m);
+ Outgoing.class.notify();
+ }
+ }
+
+ private static boolean attempt(Message m) {
+ InetAddress[] mx = getMailExchangerIPs(m.envelopeTo.host);
+ if (mx.length == 0) {
+ Log.warn("could not resolve " + m.envelopeTo.host + "; bouncing it\n" + m.summary());
+ send(m.bounce("could not resolve " + m.envelopeTo.host));
+ return true;
+ }
+ if (new Date().getTime() - m.arrival.getTime() > 1000 * 60 * 60 * 24 * 5) {
+ Log.warn("could not send message after 5 days; bouncing it\n" + m.summary());
+ send(m.bounce("could not send for 5 days"));
+ return true;
+ }
+ for(int i=0; i
+ - Original Architect
+ - Ibex Engine
+ - Most of the widget set
+
+[david] David Crawshaw
+ - Widget Czar
+ - Fixes to slider.ibex
+
+[corey] Corey Jewett
+ - Patch to make build.xml not depend on task
+
+[ejones] Evan Jones
+ - Google demo
+ - Dynamic Trees
+
diff --git a/upstream/org.ibex.core/CHANGES b/upstream/org.ibex.core/CHANGES
new file mode 100644
index 0000000..c40c796
--- /dev/null
+++ b/upstream/org.ibex.core/CHANGES
@@ -0,0 +1,1066 @@
+
+== 2002 =================================================================
+
+10-Apr megacz Box.java, Surface.java: fixed rendering glitch caused by
+ lastDirtiedTimeStamp hack.
+
+10-Apr megacz org/xwt/html/p.xwt: Improved flow performance.
+
+10-Apr megacz org/xwt/Box.java: fixed a bug that prevented regions from being
+ dirtied properly when boxes are removed from the tree and then
+ re-added.
+
+10-Apr megacz org/xwt/Box.java, org/xwt/SpecialBoxProperties.java:
+ hshrink/vshrink are no longer implemented by
+ manipulating dmax; the prerender pass understands them
+ natively.
+
+11-Apr megacz org/xwt/server/HTTP.java: fixed some CDATA misbehaviors.
+
+11-Apr megacz org/xwt/html/p.xwt, org/xwt/html/test.xwt: improved HTML
+ rendering; we can now render pretty much any
tag
+
+13-Apr megacz README: updated to note that build process requires
+ libgcj.jar even for jvm builds.
+
+21-Apr megacz src/org/xwt/plat/GCJ.xml: removed -fno-rtti
+
+21-Apr megacz src/org/xwt/Surface.java: workaround for GCJ PR java/6393
+
+22-Apr megacz src/org/xwt/plat/Java2.java: fixed bug that caused
+ RasterFormatException on jdk1.4
+
+22-Apr megacz README: included instructions on how to build without gcc.
+
+26-Apr megacz src/org/xwt/Main.java: included text description on splash screen
+
+26-Apr megacz src/org/xwt/plat/Win32.xml, src/org/xwt/plat/Java2.xml:
+ adjusted dist / signature process.
+
+26-Apr megacz README: included printStackTrace() patch
+
+26-Apr megacz src/org/xwt/XWT.java: fixed bug 53
+
+26-Apr megacz src/org/xwt/TinySSL.java: fixed PKCS1 bug
+
+26-Apr megacz build.xml: staging/production push process
+
+26-Apr megacz src/org/xwt/tasks/BashTask.java: now checks exit codes
+
+26-Apr megacz src/org/xwt/tasks/BashTask.java: added ssh support
+
+27-Apr megacz README: added file locking patch
+
+27-Apr megacz [lots of files]: introduced notion of buildid's, to make
+ XWT upgrades work more smoothly, and to prevent problems
+ with browser/plugin caches.
+
+27-Apr megacz JSObject.java: added extra debugging info
+
+27-Apr megacz XWT.java, Platform.java, Main.java, Java2.java,
+ Win32.java, Win32.cc, faq.html: added support for
+ xwt.newBrowserWindow()
+
+27-Apr megacz Surface.java, Box.java: nuked dirtiedTimeStamp
+ altogether; it caused more problems than it solved.
+
+27-Apr megacz reference.html: Changed capitalization of
+ faultstring/faultcode for xwt.soap() to match SOAP spec.
+
+27-Apr megacz XWT.java, Box.java: fixed ConversionError bug
+
+27-Apr megacz Platform.java: added a 3-pixel minimum for a font's
+ descent -- ensures that we have space for underlining.
+
+28-Apr megacz Template.java: fixed a bug where would
+ misbehave if used on a scriptless
+
+28-Apr megacz SOAP.java, XMLRPC.java: Base64 elements are now decoded
+ and returned as String's.
+
+28-Apr megacz Platform.java: fixed bug that caused italicized text to
+ automatically become bold.
+
+29-Apr megacz Box.java: moved underline up by one pixel (goes with
+ last change).
+
+29-Apr megacz Main.java: new splashscreen, upped the gamma, broke off
+ SplashScreen.create() into its own Message
+
+29-Apr megacz Surface.java: took blitDirtyScreenRegions() out of
+ Dirty(); replaced with Refresh(). This was causing a
+ thread hazard on Win32, resulting in Bug 21 (closed)
+
+29-Apr megacz Win32.cc: removed redundant Refresh(), removed debugging
+ statement.
+
+29-Apr megacz JSObject.java: added debugging output for an error I've
+ been seeing.
+
+29-Apr megacz src/org/xwt/themes/monopoly: overhauled the menu and
+ tree widgets.
+
+29-Apr megacz Main.java: fixed splashscreen refresh bug.
+
+30-Apr megacz window_manager_interaction.xwt: added UnMinimize/UnMaximize
+
+30-Apr megacz Chess.java, XServer.java: removed unused imports
+
+30-Apr megacz AWT.java: only create reserve Component if an
+ AWTDoubleBuffer gets created, prep for Java14, broke out
+ InnerFrame/InnerWindow and added exception handler to
+ cope with new Java1.4 HeadlessException.
+
+30-Apr megacz Java12.java, Java12.xml, Java14.java: added Java14 support
+
+30-Apr megacz Box.java: made dirty() public for Java14
+
+30-Apr megacz Platform.java: updated to detect Java14
+
+03-Apr megacz Main.java: fixed a bug that could cause >100% instantiation
+
+03-Apr megacz HTTP.java, Main.java, Platform.java, SOAP.java,
+ XMLRPC.java, Java12.java, Win32.cc, Win32.java: new HTTP
+ architecture, first implementation of proxy support.
+
+05-Apr megacz HTTP.java, Main.java, Platform.java, SOAP.java,
+ TinySSL.java, XMLRPC.java, Java12.java, Win32.java:
+ improved proxy support.
+
+06-Apr megacz Box.java: fixed a bug relating to shrink
+
+07-Apr megacz Box.java: thisbox[n] = null wasn't doing anything
+
+15-May megacz Template.java, Static.java: fixed bug where xwt.static
+ wouldn't work for packages whose name included the token
+ "xwt".
+
+15-May megacz XWT.java: added xwt.screen{Width,Height}
+
+15-May megacz JSObject.java: added extra logic to discriminate between
+ public and private puts.
+
+15-May megacz Box.java: added setLimits() call, refined cmin-updating
+ logic, added extra logging in the event of a
+ PosChange/SizeChange infinite loop.
+
+15-May megacz HTTP.java: fixed Host: bug
+
+15-May megacz Platform.java: added GCJ-Linux platform detection logic,
+ setLimits() logic.
+
+15-May megacz Resources.java: fixed a bug in download-size calculation.
+
+15-May megacz SpecialBoxProperty.java: added required extra dirty()
+ call, extra checking on textcolor.
+
+15-May megacz Surface.java: key presses/releases only go to visible
+ children, setLimits()
+
+15-May megacz XMLRPC.java: call() is now synchronized
+
+15-May megacz Win32.xml, GCJ.xml, GCJHTask.java: ${gcc-prefix} now ends
+ with a dash.
+
+15-May megacz Win32.java: handled the oddball all-protocol proxy case
+
+18-May megacz Static.java: removed redundant debug message
+
+18-May megacz JSObject.java: fixed a public/private bug
+
+28-May megacz HTTP.java: ugly hack to cope with Microsoft CARP PAC scripts
+
+28-May megacz Main.java: spelling fix
+
+01-Jun megacz Platform.java: extra checks on URL well-formedness
+
+01-Jun megacz AWT.java: file dialog suggestion bugfix
+
+01-Jun megacz Main.java: custom splash screen support
+
+01-Jun megacz POSIX.cc, POSIX.java: finally completed POSIX support
+
+01-Jun megacz Win32.cc: attempted fix for international keyboards
+
+05-Jun megacz Box.java: bugfix to ensure that negative regions are never filled
+
+05-Jun megacz Main.java: now require filename to end with .xwar
+
+05-Jun megacz Ibex.java: ibex.fileSeparator, ibex.homeDir, ibex.tempDir,
+ ibex.recursivePrintObject
+
+05-Jun megacz AWT.java: fix for non-US keyboards
+
+14-Jun megacz HTML.java, Ibex.java: introduced ibex.parseHTML()
+
+16-Jun megacz Resources.java: eliminated some annoying warning messages
+
+16-Jun megacz Template.java: fixed erroneous attribute checks for numbers and image/border
+
+16-Jun megacz HTTP.java: diabled ibex-httpProxy, etc
+
+23-Jun megacz Box.java: JPEG hack, fastpath rendering bugfix,
+ fixedaspect, improved >500 Pos/SizeChange loop breakout
+
+23-Jun megacz HTTP.java: removed System.out.println()
+
+23-Jun megacz Main.java: lightened splash screen
+
+23-Jun megacz Platform.java: no longer invokes System.exit() from
+ applet, setTcpNoDelay() in Platform.getSocket()
+
+23-Jun megacz SOAP.java, XMLRPC.java: bugfix for CDATA content in random elements
+
+23-Jun megacz SpecialBoxProperty.java: fixedaspect, warning about non-self-redirect root boxes
+
+23-Jun megacz Java12.java: removed wierd BufferedImage hackaround;
+ jdk1.4 doesn't need it and it was causing wierd rendering glitches
+
+23-Jun megacz POSIX.cc: hackaround for wierd XFreeColormap() failure
+
+23-Jun megacz Win32.java: fix for YET ANOTHER wacky Win32 proxy string format
+
+24-Jun megacz MessageQueue.java: fix to MessageQueue to improve
+ XML-RPC/SOAP performance
+
+01-Jul megacz DERObjectIdentifier.java: fix to work around GCC compiler bug
+
+01-Jul megacz XWF.java: fixed an int[] sharing problem
+
+01-Jul megacz XMLRPC.java, NativeDate.java: XMLRPC can now send dateTime's
+
+01-Jul megacz POSIX.java, POSIX.cc: initial window size, DISPLAY
+ string logging, http_proxy environment variable checking, font fixes
+
+01-Jul megacz Platform.java: initial window size fixes
+
+01-Jul megacz HTTP.java: we no longer insert the IP into the URL.
+
+01-Jul megacz TinySSL.java, HTTP.java: fixed SSL-over-Proxy
+
+01-Jul megacz AWT.java: added dialog for critical aborts
+
+01-Jul megacz Main.java: added browser detection output
+
+02-Jul david bevel.ibex: hpad/vpad must be greater than thickness
+
+02-Jul david colorpicker.ibex, slider.ibex, slider_base.ibex: Implemented
+ slider.ibex (limits, value display, quantization) and made
+ major improvements/fixed to colorpicker.
+
+15-Jul megacz HTTP.java: implemented isInNet(), weekdayRange(), now
+ checks environment vars for proxies
+
+15-Jul megacz PNG.java: fixed 8bpp bug
+
+15-Jul megacz Platform.java, Win32.cc, Win32.java, POSIX.java:
+ getEnv(), engine knows its own build-id
+
+15-Jul megacz POSIX.cc: ignore SIGPIPE in case the browser that
+ launched us closes
+
+15-Jul megacz SpecialBoxProperty.java: redirect property,
+ color/textcolor error message tweak.
+
+15-Jul megacz TinySSL.java: speed improvement on certificate checks
+
+15-Jul megacz XMLRPC.java: workaround for bug in Zope XML-RPC server
+
+15-Jul megacz AWT.java: print out color depth
+
+15-Jul megacz BashTask: print everything on stdout
+
+15-Jul megacz JSObject.java: added JSFunction helper
+
+15-Jul megacz GCJ.xml, Ibex.java, Template.java, Static.java,
+ Resources.java, splash.ibex, lithium.png, Main.java,
+ Box.java: builtin.xwar, loadArchive(), prefetchImage()
+
+18-Jul megacz XMLRPC.java, MessageQueue.java: attempted to improve
+ responsiveness of UI by yielding on network ops if
+ non-thread events are in the queue.
+
+18-Jul megacz Platform.java: never create or permit windows smaller
+ than the scar.
+
+18-Jul megacz Surface.java: improved shift modifier handling,
+ prohibited windows smaller than scar.
+
+18-Jul megacz Ibex.java: added write-only 'proxyAuthorization' field.
+
+18-Jul megacz AWT.java: boosted font size by 2 points on Java12
+
+18-Jul megacz GCJ.xml: builtin.xwar support
+
+18-Jul megacz POSIX.cc: better capslock handling
+
+18-Jul megacz Win32.cc: fixed MINMAXINFO bug
+
+18-Jul megacz Win32.java: changed dialog mapping to MS Sans Serif
+
+18-Jul megacz Platform.java: don't try to getEnv() on Win w/ jdk<1.4
+
+18-Jul megacz HTTP.java: Basic Proxy-Authorization support
+
+19-Jul megacz POSIX.java: font fix
+
+19-Jul megacz TinySSL.java: workaround for a GCJ bug
+
+20-Jul megacz PNG.java: ignore transparency chunk if encountered
+ before palette chunk
+
+25-Jul megacz Box.java: fixed bug related to adding/removing boxes
+
+30-Jul corey build.xml: patch to make build.xml not depend on
+
+06-Aug megacz Context.java: workaround for GCJ bug which causes
+ MessageFormatter to choke on { and }
+
+06-Aug megacz MessageQueue.java: fixed MessageQueueWatcher so that it
+ works even when an infinite loop occurs in a _SizeChange
+ or _PosChange trap.
+
+06-Aug megacz Platform.java: removed WPAD detection, HTTP.Proxy => Proxy.
+
+06-Aug megacz Static.java, Resources.java: fixed ibex.static bug.
+
+06-Aug megacz SpecialBoxProperty.java: thisbox.root now returns null
+ if thisbox is a root box.
+
+06-Aug megacz Surface.java: fixed button 1/3 swapping bug
+
+06-Aug megacz TinySSL.java: option to ignore untrusted certs, fixed GCJ bug
+
+06-Aug megacz AWT.java: removed blitDirtyScreenRegions() due to
+ deadlock, compensated for MacOSX button-swapping bug.
+
+06-Aug megacz Java12.java: disabled focusmanager so we can recieve tab
+ keypresses.
+
+06-Aug megacz Win32.java: HTTP.ProxyInfo => Proxy
+
+06-Aug megacz JSObject.java: constructor now takes sealed argument
+
+06-Aug megacz POSIX.java: font mapper now prefers ISO_8559-1 fonts
+
+06-Aug megacz Box.java: use new HTTP interface, getIds() only returns children
+
+06-Aug megacz Ibex.java: encodeURI, decodeURI, new HTTP stack, calling
+ newBox in the foreground thread is deprecated.
+
+06-Aug megacz XMLRPC.java: new HTTP stack, better verbose output
+
+06-Aug megacz SOAP.java: new HTTP stack
+
+06-Aug megacz HTTP.java: total rewrite: BasicAuth, DigestAuth, KeepAlive, Pipelining.
+
+09-Aug megacz SpecialBoxProperty.java: changed behavior of thisbox.root
+
+09-Aug megacz Win32.java, Win32.cc: hack to avoid strange race condition in Win32 GDI
+
+10-Aug megacz HTML.java, html.ibex: fixed HTML widget to handle unclosed
tags
+
+10-Aug megacz TinySSL.java: fixed vulnerability to Mike Benham's attack
+
+16-Aug megacz Box.java: fixed a bug that was causing lockups on Win32...
+
+16-Aug megacz Surface.java, Main.java: don't scar the splash screen
+
+16-Aug megacz AWT.java, Java12.java, Java14.java: fixed jdk1.4 bug
+ with keypresses and frameless windows.
+
+16-Aug megacz Ibex.java, SpecialBoxProperty.java: box.apply()
+
+17-Aug megacz Box.java, SpecialBoxProperty.java, Surface.java: code to
+ prevent engine from quitting when changing the last
+ remaining surface from a frame to a window or vice
+ versa.
+
+17-Aug megacz Vec.java: fixed infinite loop
+
+17-Aug megacz Trap.java: switched allTraps from a Vector to a Hashtable
+
+17-Aug megacz Main.java: initial instantiation now happens in a background thread
+
+17-Aug megacz Template.java: fixed a bug that was keeping retheme() from working
+
+20-Aug megacz Box.java: fixed rounding error
+
+21-Aug megacz PalmOS.java, PalmOS.xml, jump.jar: began work on PalmOS port.
+
+24-Aug megacz HTTP.java, SOAP.java: fixed bug which caused
+ miscalculation of the Content-Length header.
+
+24-Aug megacz POSIX.java: fixed cut/paste bug
+
+02-Sep megacz AWT.java: report corner of window, not corner of decorative frame
+
+02-Sep megacz Surface.java, Main.java: don't quit due to disposal of
+ last surface unless initialization is complete.
+
+03-Sep megacz XWF.java: code to handle .xwf's that don't include the
+ underscore character.
+
+15-Sep megacz Box.java: committed a change that I forgot to commit
+ dealing with box removal.
+
+15-Sep megacz HTTP.java: fixed end-of-header detection
+
+15-Sep megacz Platform.java: disable 2d acceleration on Mac OS X 10.2
+
+15-Sep megacz Template.java: added callback for retheme operation
+
+15-Sep megacz Ibex.java: new retheme() syntax
+
+23-Sep megacz SpecialBoxProperty.java: invisible now returns true if
+ any of the box's ancestors is invisible.
+
+23-Sep megacz SpecialBoxProperty.java, Box.java, Surface.java:
+ expanded dimensions to 32 bits, fixed bug in behavior of
+ hshrink/vshrink, don't deliver events to root first.
+
+23-Sep megacz HTTP.java: never skip(0)
+
+03-Oct megacz ByteStream.java: fixed null pointer problem
+
+03-Oct megacz Proxy.java: fixed bug in shMatchExp()
+
+03-Oct megacz Box.java, SpecialBoxProperty.java: fixed shrink
+ behavior, deliver clicks to box first (not root), id is
+ a readable attribute
+
+03-Oct megacz Ibex.java: made recursivePrintObject public so we can
+ access it.
+
+11-Oct megacz POSIX.cc: fixed a bug that was causing phantom messages
+ after window deletion
+
+11-Oct megacz README, Resources.java, GCJ.xml, jazz/*: upgraded to gcj
+ 3.3; eliminated jazzlib; introduced compile hack because
+ 3.3 miscompiles Trap.java
+
+15-Oct megacz Resources.java: hackaround for broken libgcj java.util.zip
+
+15-Oct megacz Arguments.java, Trap.java: arguments.trapname
+
+30-Oct megacz SpecialBoxProperty.java: fixed mouse event propagation
+
+05-Feb megacz X11.cc X11.java POSIX.cc POSIX.java Linux.xml: separated
+ POSIX from X11.
+
+06-Feb megacz X11.java: fixed a typo
+
+11-Feb megacz org/bouncycastle/*: upgraded bouncycastle
+
+11-Feb megacz README: updated gcc instructions and patches
+
+11-Feb megacz Log.java: refactored printing code in prep for inline logging
+
+11-Feb megacz X11.java: bugfix for stupid typos
+
+11-Feb megacz Picture.java, DoubleBuffer.java, Win32.java, AWT.java:
+ made Picture and DoubleBuffer abstract classes rather
+ than interfaces
+
+11-Feb megacz Trap.java: changed System.out.println to Log.log
+
+11-Feb megacz TinySSL.java: changed System.out.println to Log.log
+
+11-Feb megacz PNG.java: changed System.out.println to Log.log
+
+11-Feb megacz splash.ibex: added "press Esc to quit" message
+
+11-Feb megacz Resources.java: removed libgcj hack; 3.3 fixed the problem
+
+11-Feb megacz Platform.java: System.out->System.err, POSIX->X11
+
+11-Feb megacz GCJ.xml: added optimizations, linker path
+
+11-Feb megacz POSIX.java: removed extra debugging output
+
+11-Feb megacz POSIX.cc: added required #includes
+
+11-Feb megacz Linux.xml: removed needless -ldl
+
+11-Feb megacz Main.java: System.out->System.err
+
+11-Feb megacz DoubleBuffer.java: added 'abstract' keyword
+
+11-Feb megacz Picture.java: added 'abstract' keyword
+
+15-Feb megacz GCJ.xml, Linux.xml, Win32.xml: fixed linkage bug
+
+25-Feb megacz README: switched build process from ant to make
+
+25-Feb megacz Makefile, README: merged Java12 and Java14 into Java2
+
+25-Feb megacz README, Makefile: added next.build and next-build target
+
+25-Feb megacz Makefile: added new-release target
+
+25-Feb megacz Makefile: added nice -n 19 for new-release builds
+
+25-Feb megacz Makefile: fixed typo
+
+26-Feb megacz Makefile: added dist, nohup-dist targets; removed new-release
+
+27-Feb megacz ant: removed ant script
+
+27-Feb megacz Makefile: corrected spacing of build messages
+
+27-Feb megacz Makefile, Makefile: tweaked make dist
+
+27-Feb =========== build 02B6 ================================================
+27-Feb megacz Makefile: limited cvs emails to 200 lines
+
+
+27-Feb =========== build 02B7 ================================================
+
+27-Feb =========== build 02B8 ================================================
+
+27-Feb =========== build 0272 ================================================
+27-Feb megacz Makefile: simplified host detection
+
+27-Feb megacz Makefile: typo fix
+
+
+27-Feb =========== build 0273 ================================================
+27-Feb megacz Makefile: strip binaries in make dist
+
+
+27-Feb =========== build 0274 ================================================
+27-Feb megacz Makefile: master.dist.ibex.org
+
+27-Feb megacz Makefile, Makefile, Makefile: none
+
+
+27-Feb =========== build 0275 ================================================
+27-Feb megacz Makefile: added neglected shell keyword
+
+01-Mar megacz Makefile: gcj doesn\'t need a target prefix
+
+04-Mar megacz Makefile, Makefile: preliminary Darwin support
+
+10-Mar megacz Makefile: autotag on make dist
+
+10-Mar megacz Makefile: autotag on make dist
+
+10-Mar megacz Makefile: make dist now creates source tarballs
+
+10-Mar megacz Makefile: make dist now creates source tarballs
+
+
+10-Mar =========== build 0700 ================================================
+10-Mar megacz Makefile: fixed Makefile to make source tarballs
+
+10-Mar megacz Makefile: fixed Makefile to make source tarballs
+
+11-Mar megacz Makefile: lithium shouldn't support darwin
+
+11-Mar megacz Makefile: HEAD shouldn't support Darwin yet
+
+11-Mar megacz Makefile: none
+
+12-Mar megacz Makefile: bootclasspath and fastjar auto-detection
+
+12-Mar megacz Makefile: bootclasspath and fastjar auto-detection
+
+13-Mar megacz Makefile: jikes autodetection, jikes optional
+ build, javac fallback
+
+17-Mar megacz Makefile: added propose-patch makefile target
+
+17-Mar megacz Makefile: added propose-patch makefile target
+19-Mar david README: test
+
+19-Mar david Makefile: jikes compile on stock os x install
+
+24-Mar megacz Makefile: added Davids jikes patch
+
+24-Mar megacz Makefile: added v option to tar
+
+26-Mar andrew XML.java: applied comment counter fix
+ patch (commentcountfix1.diff)
+
+26-Mar megacz Makefile: gosset -> serverbeach
+
+26-Mar megacz Makefile: added Darwin precomp header hack
+
+26-Mar megacz Makefile: added Darwin precomp header hack
+
+26-Mar megacz Makefile: gosset -> serverbeach
+
+26-Mar megacz Makefile: tab bug
+
+26-Mar megacz Makefile: fixed bug for compatability with BSD make
+
+26-Mar megacz Makefile: fixed bug for compatability with BSD make
+
+26-Mar megacz Makefile: removed the fancy single-line build
+ outputs
+
+26-Mar megacz Makefile: removed the fancy single-line build
+ outputs
+
+26-Mar megacz Makefile: Removed -O9; will add later
+
+26-Mar megacz Makefile: tried O2
+
+29-Mar megacz Main.java: ignore argument to -l
+
+29-Mar megacz Makefile: correct use of -m in jar invocation
+
+29-Mar megacz Main.java: ignore argument to -l
+
+29-Mar megacz Makefile: correct use of -m in jar invocation
+
+29-Mar megacz Java2.java: weakened binding of Java14Surface to
+ keep 1.3.x working
+
+29-Mar megacz Java2.java: weakened binding of Java14Surface to
+ keep 1.3.x working
+
+30-Mar megacz Makefile: trying -O0 since we're still getting
+ faults
+
+30-Mar megacz Makefile: harmonized self-contradictory
+ optimization settings
+
+30-Mar megacz Makefile: harmonized self-contradictory
+ optimization settings
+
+31-Mar megacz Makefile: rolled back to gcj 19-Oct-2002
+
+31-Mar megacz Makefile: rolled back to gcj 19-Oct-2002
+
+31-Mar megacz Makefile: trying to fix cvs update
+
+31-Mar megacz Makefile: more attempts at solving the cvs update
+ problem
+
+31-Mar megacz Makefile: fixed gcjh so that it can be found before
+ gcc-linux is built
+
+31-Mar megacz Makefile: fixed gcjh so that it can be found before
+ gcc-linux is built
+
+09-Apr megacz jpegsrc.v6b.tar.gz: added ijg jpeg decoder source
+ tarball
+
+09-Apr megacz HTTP.java: fixed a bug that prevented
+ HTTPInputStream from signalling EOF
+
+09-Apr megacz Makefile: fixed gcc update script
+
+09-Apr megacz Makefile: fixed gcc update script
+
+09-Apr megacz Makefile, jmorecfg.h.patch, Box.java, Platform.java, Resources.java, AWT.java, GCJ.cc, GCJ.java, Win32.cc:
+ added jpeg support
+
+09-Apr megacz Makefile: fixed make dist
+
+09-Apr megacz Makefile: fixed make dist again
+
+
+09-Apr =========== build 0701 ================================================
+09-Apr megacz Makefile: fixed make dist
+
+
+09-Apr =========== build 0702 ================================================
+
+09-Apr =========== build 0703 ================================================
+10-Apr megacz Makefile: fixed linkage problem
+
+
+10-Apr =========== build 0704 ================================================
+
+10-Apr =========== build 0705 ================================================
+10-Apr megacz GCJ.cc: forgot extern C for jpeglib
+
+
+10-Apr =========== build 0706 ================================================
+19-Apr megacz Makefile, README, Main.java, Platform.java, XML.java, GCJ.cc, GCJ.java, Java2.java, POSIX.cc, Win32.cc, X11.cc:
+ finally -- working Darwin support
+
+19-Apr megacz Darwin.cc, Darwin.java, Linux.java: added more plat
+ files
+
+19-Apr megacz Makefile, Makefile, jmorecfg.h.patch, jpegsrc.v6b.tar.gz, Box.java, HTTP.java, Platform.java, Resources.java, AWT.java, GCJ.cc, GCJ.java, Win32.cc, X11.cc:
+ merged in HEAD
+
+19-Apr megacz Makefile, Main.java, Platform.java, Darwin.cc, Darwin.java, GCJ.cc, GCJ.java, Linux.java, POSIX.cc, Win32.cc, X11.cc:
+ merge from Darwin branch
+
+19-Apr megacz gcc.patch: added Brian Alliets gcc patch
+
+19-Apr megacz JS.java, Resources.java: GUT_branch
+
+19-Apr megacz Makefile: added line to apply gcc.patch; updated
+ gcc3.3 to 08-Apr-2003
+
+20-Apr megacz gcc.patch: fixed the patch so it applies properly
+
+22-Apr megacz Lexer.java: added the Lexer for org.ibex.js
+
+24-Apr megacz Makefile, Carbon.cc, Carbon.java: added Carbon
+ skeleton code
+
+24-Apr megacz Carbon.java: bugfix to make it compile
+
+24-Apr megacz Parser.java, Lexer.java: parser
+
+24-Apr david ByteStream.java, Resources.java, Template.java, XML.java, XMLRPC.java, Vec.java, XML.java, ByteStream.java, Resources.java, Template.java, XML.java, XMLRPC.java, Vec.java, XML.java, ByteStream.java, Resources.java, Template.java, XML.java, XMLRPC.java, Vec.java, XML.java:
+ new xml parser
+
+27-Apr megacz Makefile: fixed host/target detection problem
+
+28-Apr megacz Template.java: removed 150-char line length
+ restriction
+
+28-Apr megacz Template.java: removed 150-char line length
+ restriction
+
+29-Apr david Box.java: check for a moving redirect target
+
+29-Apr megacz Makefile: updated ibex cvs url
+
+29-Apr david Parser.java, TokenStream.java: HACKS for
+ 'and'/anonymous functions
+
+29-Apr megacz JS.java: New JS interface
+
+29-Apr megacz Vec.java: added push/pop
+
+29-Apr megacz Lexer.java, Parser.java: fixed lots of parser/lexer
+ bugs
+
+29-Apr megacz Box.java: font inheritance
+
+29-Apr megacz Makefile: don't build awt; speeds up gcj builds
+
+01-May megacz Makefile: forgot to disable hashsync on darwin
+
+01-May david Makefile: fix for non-linux build environments
+
+02-May megacz Lexer.java, Parser.java: parser now parses
+ splash.ibex
+
+02-May megacz JS.java, Parser.java: removed all FIXMES
+
+03-May megacz JS.java, Parser.java: more fixups
+
+03-May megacz COPYING: cleaned up the copyright declarations
+
+03-May megacz README: major cleanup of the README
+
+03-May megacz JS.java, Lexer.java, Parser.java: lexer, parser,
+ and interpreter complete but untested
+
+05-May megacz Makefile: enable-threads=posix on Darwin
+
+06-May megacz Expr.java: added expr
+
+06-May megacz Makefile: upstream: co -> head
+
+07-May megacz Expr.java: typo
+
+07-May david .cvsignore, .cvsignore, .cvsignore: added cvsignore
+ files
+
+08-May ejones Makefile, Main.java, Platform.java, Carbon.cc, Carbon.java, Win32.java:
+ Native Carbon Port: - Add Carbon implementation - Fix the
+ Makefile to build it correctly - Fix Main and Platform to call a
+ method running on the Platform class when Ibex is running. -
+ Fixed Win32 to block the main thread forever in its running
+ method.
+
+09-May david Makefile: fixes for upstream references
+
+11-May megacz Vec.java: made Vec final so methods get inlined
+
+11-May megacz Java2.java: removed references to Main.applet,
+ re-enabled dirtying on Java1.4-for-OSX
+
+11-May megacz Expr.java, JS.java, Lexer.java, Parser.java, package.html:
+ more JS code
+
+11-May megacz edit.ibex: updated to David's latest edit widget
+
+11-May megacz scar.png: scar image now lives in org.ibex.builtin
+
+11-May megacz Main.java: removed applet support, moved scar to
+ org.ibex.builtin
+
+11-May megacz edit_lib.ibex: added Davids ibex.standard.lib.edit
+
+11-May megacz Arguments.java, BaseFunction.java, BinaryDigitReader.java, ClassDefinitionException.java, ClassNameHelper.java, ClassOutput.java, Context.java, ContextListener.java, DToA.java, DebuggableEngineImpl.java, DefaultErrorReporter.java, Delegator.java, EcmaError.java, ErrorReporter.java, EvaluatorException.java, FlattenedObject.java, Function.java, FunctionNode.java, FunctionObject.java, IRFactory.java, IdFunction.java, IdFunctionMaster.java, IdScriptable.java, ImporterTopLevel.java, InterpretedFunction.java, InterpretedScript.java, Interpreter.java, InterpreterData.java, InterpreterFrame.java, Invoker.java, JavaMembers.java, JavaScriptException.java, Label.java, LabelTable.java, LazilyLoadedCtor.java, LineBuffer.java, ListenerArray.java, LocalVariable.java, NativeArray.java, NativeBoolean.java, NativeCall.java, NativeDate.java, NativeError.java, NativeFunction.java, NativeGlobal.java, NativeJavaArray.java, NativeJavaClass.java, NativeJavaConstructor.java, NativeJavaMethod.java, NativeJavaObject.java, NativeJavaPackage.java, NativeMath.java, NativeNumber.java, NativeObject.java, NativeScript.java, NativeString.java, NativeWith.java, Node.java, NodeTransformer.java, NotAFunctionException.java, Parser.java, PreorderNodeIterator.java, PropertyException.java, RegExpProxy.java, Script.java, ScriptRuntime.java, Scriptable.java, ScriptableObject.java, SecuritySupport.java, ShallowNodeIterator.java, SourceTextItem.java, SourceTextManager.java, Synchronizer.java, TokenStream.java, UintMap.java, Undefined.java, VariableTable.java, WrapHandler.java, WrappedException.java, Wrapper.java, DebugFrame.java, DebugReader.java, DebuggableEngine.java, DebuggableScript.java, Debugger.java, NativeRegExp.java, NativeRegExpCtor.java, RegExpImpl.java, SubString.java, Box.java, ByteStream.java, HTML.java, HTTP.java, MessageQueue.java, Platform.java, Proxy.java, Resources.java, SOAP.java, SpecialBoxProperty.java, Static.java, Surface.java, Template.java, ThreadMessage.java, Trap.java, XMLRPC.java, Ibex.java, AWT.java, Java12.java, JSObject.java:
+ goodbye org.mozilla.javascript, hello org.ibex.js
+
+11-May megacz msjvm.jar, netscape.jar: removing
+ msjvm.jar/netscape.jar; use reflection instead
+
+26-May megacz Box.java, Resources.java, SpecialBoxProperty.java, Static.java, Ibex.java, JS.java, Parser.java:
+ lots of progress on org.ibex.js
+
+
+26-May =========== build 0707 ================================================
+
+26-May =========== build 0708 ================================================
+02-Jun megacz Ibex.java, Lexer.java, Parser.java: more js
+ improvements
+
+03-Jun megacz JS.java, Lexer.java, Parser.java: nearly finished
+ switching to a bytecode architecture
+
+03-Jun megacz Parser.java: more js improvements
+
+03-Jun megacz Parser.java: almost done moving to bytecode
+
+03-Jun megacz Parser.java: completely switched over to bytecode
+
+
+16-Jun =========== build 0710 ================================================
+
+16-Jun =========== build 0711 ================================================
+
+16-Jun =========== build 0712 ================================================
+
+16-Jun =========== build 0713 ================================================
+
+16-Jun =========== build 0714 ================================================
+
+16-Jun =========== build 0715 ================================================
+
+17-Jun =========== build 0716 ================================================
+
+17-Jun =========== build 0717 ================================================
+
+17-Jun =========== build 0718 ================================================
+
+17-Jun =========== build 0719 ================================================
+
+17-Jun =========== build 071A ================================================
+
+17-Jun =========== build 071B ================================================
+
+17-Jun =========== build 071C ================================================
+
+17-Jun =========== build 071D ================================================
+
+17-Jun =========== build 071E ================================================
+
+17-Jun =========== build 071F ================================================
+
+17-Jun =========== build 0720 ================================================
+
+17-Jun =========== build 0721 ================================================
+
+17-Jun =========== build 0722 ================================================
+
+17-Jun =========== build 0723 ================================================
+
+17-Jun =========== build 0724 ================================================
+
+17-Jun =========== build 0725 ================================================
+
+17-Jun =========== build 0726 ================================================
+
+17-Jun =========== build 0727 ================================================
+
+17-Jun =========== build 0728 ================================================
+
+17-Jun =========== build 0729 ================================================
+
+17-Jun =========== build 072A ================================================
+
+17-Jun =========== build 072B ================================================
+
+17-Jun =========== build 072C ================================================
+
+17-Jun =========== build 072D ================================================
+
+17-Jun =========== build 072E ================================================
+
+17-Jun =========== build 072F ================================================
+
+17-Jun =========== build 0730 ================================================
+
+17-Jun =========== build 0731 ================================================
+
+18-Jun =========== build 0732 ================================================
+
+18-Jun =========== build 0733 ================================================
+
+18-Jun =========== build 0734 ================================================
+
+18-Jun =========== build 0735 ================================================
+
+18-Jun =========== build 0736 ================================================
+
+18-Jun =========== build 0737 ================================================
+
+18-Jun =========== build 0738 ================================================
+
+18-Jun =========== build 0739 ================================================
+
+18-Jun =========== build 073A ================================================
+
+05-Jul =========== build 073B ================================================
+
+05-Jul =========== build 073C ================================================
+
+05-Jul =========== build 073D ================================================
+
+05-Jul =========== build 073E ================================================
+
+05-Jul =========== build 073F ================================================
+
+05-Jul =========== build 0740 ================================================
+
+05-Jul =========== build 0741 ================================================
+
+05-Jul =========== build 0742 ================================================
+
+05-Jul =========== build 0743 ================================================
+
+05-Jul =========== build 0744 ================================================
+
+07-Jul =========== build 0745 ================================================
+
+07-Jul =========== build 0746 ================================================
+
+09-Jul =========== build 0747 ================================================
+
+09-Jul =========== build 0748 ================================================
+
+09-Jul =========== build 0749 ================================================
+
+15-Jul =========== build 074A ================================================
+
+15-Jul =========== build 074B ================================================
+
+15-Jul =========== build 074C ================================================
+
+21-Jul =========== build 074D ================================================
+
+21-Jul =========== build 074E ================================================
+
+27-Jul =========== build 074F ================================================
+
+27-Jul =========== build 0750 ================================================
+
+27-Jul =========== build 0751 ================================================
+
+25-Sep =========== build 07a0 ================================================
+
+25-Sep =========== build 07A1 ================================================
+
+25-Sep =========== build 07A2 ================================================
+
+25-Sep =========== build 07A3 ================================================
+
+25-Sep =========== build 07A4 ================================================
+
+25-Sep =========== build 07A5 ================================================
+
+25-Sep =========== build 07A6 ================================================
+
+26-Sep =========== build 07A7 ================================================
+
+26-Sep =========== build 07A8 ================================================
+
+26-Sep =========== build 07A9 ================================================
+
+26-Sep =========== build 07AA ================================================
+
+26-Sep =========== build 07AB ================================================
+
+26-Sep =========== build 07AC ================================================
+
+27-Sep =========== build 07AD ================================================
+
+27-Sep =========== build 07AE ================================================
+
+27-Sep =========== build 07AF ================================================
+
+27-Sep =========== build 07AE ================================================
+
+27-Sep =========== build 07B0 ================================================
+
+27-Sep =========== build 07B1 ================================================
+
+14-Oct =========== build 07B2 ================================================
+
+14-Oct =========== build 07B3 ================================================
+
+15-Oct =========== build 07B4 ================================================
+
+16-Oct =========== build 07B5 ================================================
+
+16-Oct =========== build 07B6 ================================================
+
+16-Oct =========== build 07B7 ================================================
+
+16-Oct =========== build 07B8 ================================================
+
+16-Oct =========== build 07B9 ================================================
+
+16-Oct =========== build 07BA ================================================
+
+18-Oct =========== build 07BB ================================================
+
+18-Oct =========== build 07BC ================================================
+
+18-Oct =========== build 07BD ================================================
+
+18-Oct =========== build 07BE ================================================
+
+19-Oct =========== build 07BF ================================================
+
+19-Oct =========== build 07C0 ================================================
+
+21-Oct =========== build 07C1 ================================================
+
+21-Oct =========== build 07C2 ================================================
+
+21-Oct =========== build 07C3 ================================================
+
+21-Oct =========== build 07C4 ================================================
+
+27-Oct =========== build 07C5 ================================================
+
+28-Oct =========== build 07C6 ================================================
+
+28-Oct =========== build 07C7 ================================================
+
+28-Oct =========== build 07C8 ================================================
+
+28-Oct =========== build 07C9 ================================================
+
+28-Oct =========== build 07CA ================================================
+
+28-Oct =========== build 07CB ================================================
+
+28-Oct =========== build 07CC ================================================
+
+28-Oct =========== build 07CD ================================================
+
+28-Oct =========== build 07CE ================================================
+
+28-Oct =========== build 07CF ================================================
+
+28-Oct =========== build 07D0 ================================================
+
+28-Oct =========== build 07D0 ================================================
+
+18-Jan =========== build 07D2 ================================================
+n18-Jan =========== build 07D3 ================
+n19-Jan =========== build 07D4 ================
+n19-Jan =========== build 07D5 ================
+n20-Jan =========== build 07D6 ================
+n20-Jan =========== build 07D7 ================
+n20-Jan =========== build 07D8 ================
+n20-Jan =========== build 07D9 ================
+n20-Jan =========== build 07DA ================
+n20-Jan =========== build 07DB ================
+n20-Jan =========== build 07DC ================
diff --git a/upstream/org.ibex.core/COPYING b/upstream/org.ibex.core/COPYING
new file mode 100644
index 0000000..aaa7d80
--- /dev/null
+++ b/upstream/org.ibex.core/COPYING
@@ -0,0 +1,2033 @@
+================================================================================
+The contents of this distribution are copyrighted and licensed as follows:
+================================================================================
+
+ - src/org/ibex/TinySSL.java is Copyright 2002 Adam Megacz, and is
+ licensed under the GNU Library General Public License; you may
+ reuse it in closed source applications under certain conditions;
+ see the LGPL (attached below).
+
+ - src/org/ibex/js/Lexer.java was partially derived from the Rhino
+ JavaScript interpreter; hence is covered by the Netscape Public
+ License.
+
+ - src/org/mozilla/** contains the Rhino JavaScript Interpreter,
+ which is Copyright Netscape Communications Corporation, and is
+ licensed under the Netscape Public License.
+
+ - lib/netscape.jar and lib/signtool are Copyright Netscape
+ Communications Corporation
+
+ - lib/msjvm.jar, lib/cabarc.exe, lib/signcode.exe, and
+ lib/guidgen.exe are Copyright Microsoft Corporation
+
+ - lib/javago is a compiled binary copy of the JavaGO global
+ optimizer, written by Konstantin Knizhnik and "is freeware and is
+ distributed without any restrictions on its usage"
+
+ - src/org/ibex/PNG.java is Copyright (c) 1997, Jason
+ Marshall. All Rights Reserved. Additional licensing terms are
+ contained in that file.
+
+ - src/org/ibex/GIF.java is Copyright D. J. Hagberg, Jr. Additional
+ licensing terms are contained in that file.
+
+ - the contents of src/org/bouncycastle is Copyright (c) 2000-2002 The
+ Legion Of The Bouncy Castle (http://www.bouncycastle.org); licensing
+ terms are available at http://www.bouncycastle.org/license.html
+
+ - All other files are Copyright 2003 Adam Megacz, all rights
+ reserved, all disclaimable warranties disclaimed.
+
+ You may redistribute and/or modify the Ibex engine (src/org/ibex/
+ and all subdirectories) under the GNU General Public License
+ ("GPL", included below), version 2 ONLY (not any prior or later
+ versions), subject to the following two clarifications:
+
+ As a clarification to the General Public License, any data files
+ (.ibex, .gif, .png) loaded into a running copy of the Ibex Engine,
+ are specifically NOT considered "derivitave works" of the engine,
+ so long as they do not contain any code copied from the engine.
+
+ - The Bitstream Vera Font (upstream/vera) is under its own copyright
+
+
+========================================================================
+The GNU General Public License
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ , 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+
+
+========================================================================
+The GNU Lesser General Public License
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ , 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
+============================================================================
+The Apache Software License, Version 1.1
+
+/*
+ * ============================================================================
+ * The Apache Software License, Version 1.1
+ * ============================================================================
+ *
+ * Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any, must
+ * include the following acknowledgment: "This product includes software
+ * developed by the Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself, if
+ * and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Ant" and "Apache Software Foundation" must not be used to
+ * endorse or promote products derived from this software without prior
+ * written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache", nor may
+ * "Apache" appear in their name, without prior written permission of the
+ * Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
+ * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * on behalf of the Apache Software Foundation. For more information on the
+ * Apache Software Foundation, please see .
+ *
+ */
+
+
+
+
+=============================================================================
+The ZLIB License
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute
+it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+ distribution.
+
+
+
+================================================================================
+The Netscape Public License
+
+
+ AMENDMENTS
+
+ The Netscape Public License Version 1.1 ("NPL") consists of the
+ Mozilla Public License Version 1.1 with the following Amendments,
+ including Exhibit A-Netscape Public License. Files identified with
+ "Exhibit A-Netscape Public License" are governed by the Netscape
+ Public License Version 1.1.
+
+ Additional Terms applicable to the Netscape Public License.
+ I. Effect.
+ These additional terms described in this Netscape Public
+ License -- Amendments shall apply to the Mozilla Communicator
+ client code and to all Covered Code under this License.
+
+ II. "Netscape's Branded Code" means Covered Code that Netscape
+ distributes and/or permits others to distribute under one or more
+ trademark(s) which are controlled by Netscape but which are not
+ licensed for use under this License.
+
+ III. Netscape and logo.
+ This License does not grant any rights to use the trademarks
+ "Netscape", the "Netscape N and horizon" logo or the "Netscape
+ lighthouse" logo, "Netcenter", "Gecko", "Java" or "JavaScript",
+ "Smart Browsing" even if such marks are included in the Original
+ Code or Modifications.
+
+ IV. Inability to Comply Due to Contractual Obligation.
+ Prior to licensing the Original Code under this License, Netscape
+ has licensed third party code for use in Netscape's Branded Code.
+ To the extent that Netscape is limited contractually from making
+ such third party code available under this License, Netscape may
+ choose to reintegrate such code into Covered Code without being
+ required to distribute such code in Source Code form, even if
+ such code would otherwise be considered "Modifications" under
+ this License.
+
+ V. Use of Modifications and Covered Code by Initial Developer.
+ V.1. In General.
+ The obligations of Section 3 apply to Netscape, except to
+ the extent specified in this Amendment, Section V.2 and V.3.
+
+ V.2. Other Products.
+ Netscape may include Covered Code in products other than the
+ Netscape's Branded Code which are released by Netscape
+ during the two (2) years following the release date of the
+ Original Code, without such additional products becoming
+ subject to the terms of this License, and may license such
+ additional products on different terms from those contained
+ in this License.
+
+ V.3. Alternative Licensing.
+ Netscape may license the Source Code of Netscape's Branded
+ Code, including Modifications incorporated therein, without
+ such Netscape Branded Code becoming subject to the terms of
+ this License, and may license such Netscape Branded Code on
+ different terms from those contained in this License.
+
+ VI. Litigation.
+ Notwithstanding the limitations of Section 11 above, the
+ provisions regarding litigation in Section 11(a), (b) and (c) of
+ the License shall apply to all disputes relating to this License.
+
+ EXHIBIT A-Netscape Public License.
+
+ "The contents of this file are subject to the Netscape Public
+ License Version 1.1 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at http://www.mozilla.org/NPL/
+
+ Software distributed under the License is distributed on an "AS
+ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ rights and limitations under the License.
+
+ The Original Code is Mozilla Communicator client code, released
+ March 31, 1998.
+
+ The Initial Developer of the Original Code is Netscape
+ Communications Corporation. Portions created by Netscape are
+ Copyright (C) 1998-1999 Netscape Communications Corporation. All
+ Rights Reserved.
+
+ Contributor(s): ______________________________________.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the _____ license (the "[___] License"), in which case
+ the provisions of [______] License are applicable instead of
+ those above. If you wish to allow use of your version of this
+ file only under the terms of the [____] License and not to allow
+ others to use your version of this file under the NPL, indicate
+ your decision by deleting the provisions above and replace them
+ with the notice and other provisions required by the [___]
+ License. If you do not delete the provisions above, a recipient
+ may use your version of this file under either the NPL or the
+ [___] License."
+
+ ----------------------------------------------------------------------
+
+ MOZILLA PUBLIC LICENSE
+ Version 1.1
+
+ ---------------
+
+1. Definitions.
+
+ 1.0.1. "Commercial Use" means distribution or otherwise making the
+ Covered Code available to a third party.
+
+ 1.1. "Contributor" means each entity that creates or contributes to
+ the creation of Modifications.
+
+ 1.2. "Contributor Version" means the combination of the Original
+ Code, prior Modifications used by a Contributor, and the Modifications
+ made by that particular Contributor.
+
+ 1.3. "Covered Code" means the Original Code or Modifications or the
+ combination of the Original Code and Modifications, in each case
+ including portions thereof.
+
+ 1.4. "Electronic Distribution Mechanism" means a mechanism generally
+ accepted in the software development community for the electronic
+ transfer of data.
+
+ 1.5. "Executable" means Covered Code in any form other than Source
+ Code.
+
+ 1.6. "Initial Developer" means the individual or entity identified
+ as the Initial Developer in the Source Code notice required by Exhibit
+ A.
+
+ 1.7. "Larger Work" means a work which combines Covered Code or
+ portions thereof with code not governed by the terms of this License.
+
+ 1.8. "License" means this document.
+
+ 1.8.1. "Licensable" means having the right to grant, to the maximum
+ extent possible, whether at the time of the initial grant or
+ subsequently acquired, any and all of the rights conveyed herein.
+
+ 1.9. "Modifications" means any addition to or deletion from the
+ substance or structure of either the Original Code or any previous
+ Modifications. When Covered Code is released as a series of files, a
+ Modification is:
+ A. Any addition to or deletion from the contents of a file
+ containing Original Code or previous Modifications.
+
+ B. Any new file that contains any part of the Original Code or
+ previous Modifications.
+
+ 1.10. "Original Code" means Source Code of computer software code
+ which is described in the Source Code notice required by Exhibit A as
+ Original Code, and which, at the time of its release under this
+ License is not already Covered Code governed by this License.
+
+ 1.10.1. "Patent Claims" means any patent claim(s), now owned or
+ hereafter acquired, including without limitation, method, process,
+ and apparatus claims, in any patent Licensable by grantor.
+
+ 1.11. "Source Code" means the preferred form of the Covered Code for
+ making modifications to it, including all modules it contains, plus
+ any associated interface definition files, scripts used to control
+ compilation and installation of an Executable, or source code
+ differential comparisons against either the Original Code or another
+ well known, available Covered Code of the Contributor's choice. The
+ Source Code can be in a compressed or archival form, provided the
+ appropriate decompression or de-archiving software is widely available
+ for no charge.
+
+ 1.12. "You" (or "Your") means an individual or a legal entity
+ exercising rights under, and complying with all of the terms of, this
+ License or a future version of this License issued under Section 6.1.
+ For legal entities, "You" includes any entity which controls, is
+ controlled by, or is under common control with You. For purposes of
+ this definition, "control" means (a) the power, direct or indirect,
+ to cause the direction or management of such entity, whether by
+ contract or otherwise, or (b) ownership of more than fifty percent
+ (50%) of the outstanding shares or beneficial ownership of such
+ entity.
+
+2. Source Code License.
+
+ 2.1. The Initial Developer Grant.
+ The Initial Developer hereby grants You a world-wide, royalty-free,
+ non-exclusive license, subject to third party intellectual property
+ claims:
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by Initial Developer to use, reproduce,
+ modify, display, perform, sublicense and distribute the Original
+ Code (or portions thereof) with or without Modifications, and/or
+ as part of a Larger Work; and
+
+ (b) under Patents Claims infringed by the making, using or
+ selling of Original Code, to make, have made, use, practice,
+ sell, and offer for sale, and/or otherwise dispose of the
+ Original Code (or portions thereof).
+
+ (c) the licenses granted in this Section 2.1(a) and (b) are
+ effective on the date Initial Developer first distributes
+ Original Code under the terms of this License.
+
+ (d) Notwithstanding Section 2.1(b) above, no patent license is
+ granted: 1) for code that You delete from the Original Code; 2)
+ separate from the Original Code; or 3) for infringements caused
+ by: i) the modification of the Original Code or ii) the
+ combination of the Original Code with other software or devices.
+
+ 2.2. Contributor Grant.
+ Subject to third party intellectual property claims, each Contributor
+ hereby grants You a world-wide, royalty-free, non-exclusive license
+
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by Contributor, to use, reproduce, modify,
+ display, perform, sublicense and distribute the Modifications
+ created by such Contributor (or portions thereof) either on an
+ unmodified basis, with other Modifications, as Covered Code
+ and/or as part of a Larger Work; and
+
+ (b) under Patent Claims infringed by the making, using, or
+ selling of Modifications made by that Contributor either alone
+ and/or in combination with its Contributor Version (or portions
+ of such combination), to make, use, sell, offer for sale, have
+ made, and/or otherwise dispose of: 1) Modifications made by that
+ Contributor (or portions thereof); and 2) the combination of
+ Modifications made by that Contributor with its Contributor
+ Version (or portions of such combination).
+
+ (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
+ effective on the date Contributor first makes Commercial Use of
+ the Covered Code.
+
+ (d) Notwithstanding Section 2.2(b) above, no patent license is
+ granted: 1) for any code that Contributor has deleted from the
+ Contributor Version; 2) separate from the Contributor Version;
+ 3) for infringements caused by: i) third party modifications of
+ Contributor Version or ii) the combination of Modifications made
+ by that Contributor with other software (except as part of the
+ Contributor Version) or other devices; or 4) under Patent Claims
+ infringed by Covered Code in the absence of Modifications made by
+ that Contributor.
+
+3. Distribution Obligations.
+
+ 3.1. Application of License.
+ The Modifications which You create or to which You contribute are
+ governed by the terms of this License, including without limitation
+ Section 2.2. The Source Code version of Covered Code may be
+ distributed only under the terms of this License or a future version
+ of this License released under Section 6.1, and You must include a
+ copy of this License with every copy of the Source Code You
+ distribute. You may not offer or impose any terms on any Source Code
+ version that alters or restricts the applicable version of this
+ License or the recipients' rights hereunder. However, You may include
+ an additional document offering the additional rights described in
+ Section 3.5.
+
+ 3.2. Availability of Source Code.
+ Any Modification which You create or to which You contribute must be
+ made available in Source Code form under the terms of this License
+ either on the same media as an Executable version or via an accepted
+ Electronic Distribution Mechanism to anyone to whom you made an
+ Executable version available; and if made available via Electronic
+ Distribution Mechanism, must remain available for at least twelve (12)
+ months after the date it initially became available, or at least six
+ (6) months after a subsequent version of that particular Modification
+ has been made available to such recipients. You are responsible for
+ ensuring that the Source Code version remains available even if the
+ Electronic Distribution Mechanism is maintained by a third party.
+
+ 3.3. Description of Modifications.
+ You must cause all Covered Code to which You contribute to contain a
+ file documenting the changes You made to create that Covered Code and
+ the date of any change. You must include a prominent statement that
+ the Modification is derived, directly or indirectly, from Original
+ Code provided by the Initial Developer and including the name of the
+ Initial Developer in (a) the Source Code, and (b) in any notice in an
+ Executable version or related documentation in which You describe the
+ origin or ownership of the Covered Code.
+
+ 3.4. Intellectual Property Matters
+ (a) Third Party Claims.
+ If Contributor has knowledge that a license under a third party's
+ intellectual property rights is required to exercise the rights
+ granted by such Contributor under Sections 2.1 or 2.2,
+ Contributor must include a text file with the Source Code
+ distribution titled "LEGAL" which describes the claim and the
+ party making the claim in sufficient detail that a recipient will
+ know whom to contact. If Contributor obtains such knowledge after
+ the Modification is made available as described in Section 3.2,
+ Contributor shall promptly modify the LEGAL file in all copies
+ Contributor makes available thereafter and shall take other steps
+ (such as notifying appropriate mailing lists or newsgroups)
+ reasonably calculated to inform those who received the Covered
+ Code that new knowledge has been obtained.
+
+ (b) Contributor APIs.
+ If Contributor's Modifications include an application programming
+ interface and Contributor has knowledge of patent licenses which
+ are reasonably necessary to implement that API, Contributor must
+ also include this information in the LEGAL file.
+
+ (c) Representations.
+ Contributor represents that, except as disclosed pursuant to
+ Section 3.4(a) above, Contributor believes that Contributor's
+ Modifications are Contributor's original creation(s) and/or
+ Contributor has sufficient rights to grant the rights conveyed by
+ this License.
+
+ 3.5. Required Notices.
+ You must duplicate the notice in Exhibit A in each file of the Source
+ Code. If it is not possible to put such notice in a particular Source
+ Code file due to its structure, then You must include such notice in a
+ location (such as a relevant directory) where a user would be likely
+ to look for such a notice. If You created one or more Modification(s)
+ You may add your name as a Contributor to the notice described in
+ Exhibit A. You must also duplicate this License in any documentation
+ for the Source Code where You describe recipients' rights or ownership
+ rights relating to Covered Code. You may choose to offer, and to
+ charge a fee for, warranty, support, indemnity or liability
+ obligations to one or more recipients of Covered Code. However, You
+ may do so only on Your own behalf, and not on behalf of the Initial
+ Developer or any Contributor. You must make it absolutely clear than
+ any such warranty, support, indemnity or liability obligation is
+ offered by You alone, and You hereby agree to indemnify the Initial
+ Developer and every Contributor for any liability incurred by the
+ Initial Developer or such Contributor as a result of warranty,
+ support, indemnity or liability terms You offer.
+
+ 3.6. Distribution of Executable Versions.
+ You may distribute Covered Code in Executable form only if the
+ requirements of Section 3.1-3.5 have been met for that Covered Code,
+ and if You include a notice stating that the Source Code version of
+ the Covered Code is available under the terms of this License,
+ including a description of how and where You have fulfilled the
+ obligations of Section 3.2. The notice must be conspicuously included
+ in any notice in an Executable version, related documentation or
+ collateral in which You describe recipients' rights relating to the
+ Covered Code. You may distribute the Executable version of Covered
+ Code or ownership rights under a license of Your choice, which may
+ contain terms different from this License, provided that You are in
+ compliance with the terms of this License and that the license for the
+ Executable version does not attempt to limit or alter the recipient's
+ rights in the Source Code version from the rights set forth in this
+ License. If You distribute the Executable version under a different
+ license You must make it absolutely clear that any terms which differ
+ from this License are offered by You alone, not by the Initial
+ Developer or any Contributor. You hereby agree to indemnify the
+ Initial Developer and every Contributor for any liability incurred by
+ the Initial Developer or such Contributor as a result of any such
+ terms You offer.
+
+ 3.7. Larger Works.
+ You may create a Larger Work by combining Covered Code with other code
+ not governed by the terms of this License and distribute the Larger
+ Work as a single product. In such a case, You must make sure the
+ requirements of this License are fulfilled for the Covered Code.
+
+4. Inability to Comply Due to Statute or Regulation.
+
+ If it is impossible for You to comply with any of the terms of this
+ License with respect to some or all of the Covered Code due to
+ statute, judicial order, or regulation then You must: (a) comply with
+ the terms of this License to the maximum extent possible; and (b)
+ describe the limitations and the code they affect. Such description
+ must be included in the LEGAL file described in Section 3.4 and must
+ be included with all distributions of the Source Code. Except to the
+ extent prohibited by statute or regulation, such description must be
+ sufficiently detailed for a recipient of ordinary skill to be able to
+ understand it.
+
+5. Application of this License.
+
+ This License applies to code to which the Initial Developer has
+ attached the notice in Exhibit A and to related Covered Code.
+
+6. Versions of the License.
+
+ 6.1. New Versions.
+ Netscape Communications Corporation ("Netscape") may publish revised
+ and/or new versions of the License from time to time. Each version
+ will be given a distinguishing version number.
+
+ 6.2. Effect of New Versions.
+ Once Covered Code has been published under a particular version of the
+ License, You may always continue to use it under the terms of that
+ version. You may also choose to use such Covered Code under the terms
+ of any subsequent version of the License published by Netscape. No one
+ other than Netscape has the right to modify the terms applicable to
+ Covered Code created under this License.
+
+ 6.3. Derivative Works.
+ If You create or use a modified version of this License (which you may
+ only do in order to apply it to code which is not already Covered Code
+ governed by this License), You must (a) rename Your license so that
+ the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
+ "MPL", "NPL" or any confusingly similar phrase do not appear in your
+ license (except to note that your license differs from this License)
+ and (b) otherwise make it clear that Your version of the license
+ contains terms which differ from the Mozilla Public License and
+ Netscape Public License. (Filling in the name of the Initial
+ Developer, Original Code or Contributor in the notice described in
+ Exhibit A shall not of themselves be deemed to be modifications of
+ this License.)
+
+7. DISCLAIMER OF WARRANTY.
+
+ COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+ WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
+ DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+ THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
+ IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
+ YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
+ COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
+ OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+ ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+8. TERMINATION.
+
+ 8.1. This License and the rights granted hereunder will terminate
+ automatically if You fail to comply with terms herein and fail to cure
+ such breach within 30 days of becoming aware of the breach. All
+ sublicenses to the Covered Code which are properly granted shall
+ survive any termination of this License. Provisions which, by their
+ nature, must remain in effect beyond the termination of this License
+ shall survive.
+
+ 8.2. If You initiate litigation by asserting a patent infringement
+ claim (excluding declatory judgment actions) against Initial Developer
+ or a Contributor (the Initial Developer or Contributor against whom
+ You file such action is referred to as "Participant") alleging that:
+
+ (a) such Participant's Contributor Version directly or indirectly
+ infringes any patent, then any and all rights granted by such
+ Participant to You under Sections 2.1 and/or 2.2 of this License
+ shall, upon 60 days notice from Participant terminate prospectively,
+ unless if within 60 days after receipt of notice You either: (i)
+ agree in writing to pay Participant a mutually agreeable reasonable
+ royalty for Your past and future use of Modifications made by such
+ Participant, or (ii) withdraw Your litigation claim with respect to
+ the Contributor Version against such Participant. If within 60 days
+ of notice, a reasonable royalty and payment arrangement are not
+ mutually agreed upon in writing by the parties or the litigation claim
+ is not withdrawn, the rights granted by Participant to You under
+ Sections 2.1 and/or 2.2 automatically terminate at the expiration of
+ the 60 day notice period specified above.
+
+ (b) any software, hardware, or device, other than such Participant's
+ Contributor Version, directly or indirectly infringes any patent, then
+ any rights granted to You by such Participant under Sections 2.1(b)
+ and 2.2(b) are revoked effective as of the date You first made, used,
+ sold, distributed, or had made, Modifications made by that
+ Participant.
+
+ 8.3. If You assert a patent infringement claim against Participant
+ alleging that such Participant's Contributor Version directly or
+ indirectly infringes any patent where such claim is resolved (such as
+ by license or settlement) prior to the initiation of patent
+ infringement litigation, then the reasonable value of the licenses
+ granted by such Participant under Sections 2.1 or 2.2 shall be taken
+ into account in determining the amount or value of any payment or
+ license.
+
+ 8.4. In the event of termination under Sections 8.1 or 8.2 above,
+ all end user license agreements (excluding distributors and resellers)
+ which have been validly granted by You or any distributor hereunder
+ prior to termination shall survive termination.
+
+9. LIMITATION OF LIABILITY.
+
+ UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+ (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+ DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
+ OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
+ ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
+ CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
+ WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+ COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+ INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+ LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+ RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+ PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
+ EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
+ THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+10. U.S. GOVERNMENT END USERS.
+
+ The Covered Code is a "commercial item," as that term is defined in
+ 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+ software" and "commercial computer software documentation," as such
+ terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
+ C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
+ all U.S. Government End Users acquire Covered Code with only those
+ rights set forth herein.
+
+11. MISCELLANEOUS.
+
+ This License represents the complete agreement concerning subject
+ matter hereof. If any provision of this License is held to be
+ unenforceable, such provision shall be reformed only to the extent
+ necessary to make it enforceable. This License shall be governed by
+ California law provisions (except to the extent applicable law, if
+ any, provides otherwise), excluding its conflict-of-law provisions.
+ With respect to disputes in which at least one party is a citizen of,
+ or an entity chartered or registered to do business in the United
+ States of America, any litigation relating to this License shall be
+ subject to the jurisdiction of the Federal Courts of the Northern
+ District of California, with venue lying in Santa Clara County,
+ California, with the losing party responsible for costs, including
+ without limitation, court costs and reasonable attorneys' fees and
+ expenses. The application of the United Nations Convention on
+ Contracts for the International Sale of Goods is expressly excluded.
+ Any law or regulation which provides that the language of a contract
+ shall be construed against the drafter shall not apply to this
+ License.
+
+12. RESPONSIBILITY FOR CLAIMS.
+
+ As between Initial Developer and the Contributors, each party is
+ responsible for claims and damages arising, directly or indirectly,
+ out of its utilization of rights under this License and You agree to
+ work with Initial Developer and Contributors to distribute such
+ responsibility on an equitable basis. Nothing herein is intended or
+ shall be deemed to constitute any admission of liability.
+
+13. MULTIPLE-LICENSED CODE.
+
+ Initial Developer may designate portions of the Covered Code as
+ "Multiple-Licensed". "Multiple-Licensed" means that the Initial
+ Developer permits you to utilize portions of the Covered Code under
+ Your choice of the NPL or the alternative licenses, if any, specified
+ by the Initial Developer in the file described in Exhibit A.
+
+EXHIBIT A -Mozilla Public License.
+
+ ``The contents of this file are subject to the Mozilla Public License
+ Version 1.1 (the "License"); you may not use this file except in
+ compliance with the License. You may obtain a copy of the License at
+ http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ License for the specific language governing rights and limitations
+ under the License.
+
+ The Original Code is ______________________________________.
+
+ The Initial Developer of the Original Code is ________________________.
+ Portions created by ______________________ are Copyright (C) ______
+ _______________________. All Rights Reserved.
+
+ Contributor(s): ______________________________________.
+
+ Alternatively, the contents of this file may be used under the terms
+ of the _____ license (the "[___] License"), in which case the
+ provisions of [______] License are applicable instead of those
+ above. If you wish to allow use of your version of this file only
+ under the terms of the [____] License and not to allow others to use
+ your version of this file under the MPL, indicate your decision by
+ deleting the provisions above and replace them with the notice and
+ other provisions required by the [___] License. If you do not delete
+ the provisions above, a recipient may use your version of this file
+ under either the MPL or the [___] License."
+
+ [NOTE: The text of this Exhibit A may differ slightly from the text of
+ the notices in the Source Code files of the Original Code. You should
+ use the text of this Exhibit A rather than the text found in the
+ Original Code Source Code for Your Modifications.]
+
+
+
+================================================================================
+The Mozilla Public License
+
+ MOZILLA PUBLIC LICENSE
+ Version 1.1
+
+ ---------------
+
+1. Definitions.
+
+ 1.0.1. "Commercial Use" means distribution or otherwise making the
+ Covered Code available to a third party.
+
+ 1.1. "Contributor" means each entity that creates or contributes to
+ the creation of Modifications.
+
+ 1.2. "Contributor Version" means the combination of the Original
+ Code, prior Modifications used by a Contributor, and the Modifications
+ made by that particular Contributor.
+
+ 1.3. "Covered Code" means the Original Code or Modifications or the
+ combination of the Original Code and Modifications, in each case
+ including portions thereof.
+
+ 1.4. "Electronic Distribution Mechanism" means a mechanism generally
+ accepted in the software development community for the electronic
+ transfer of data.
+
+ 1.5. "Executable" means Covered Code in any form other than Source
+ Code.
+
+ 1.6. "Initial Developer" means the individual or entity identified
+ as the Initial Developer in the Source Code notice required by Exhibit
+ A.
+
+ 1.7. "Larger Work" means a work which combines Covered Code or
+ portions thereof with code not governed by the terms of this License.
+
+ 1.8. "License" means this document.
+
+ 1.8.1. "Licensable" means having the right to grant, to the maximum
+ extent possible, whether at the time of the initial grant or
+ subsequently acquired, any and all of the rights conveyed herein.
+
+ 1.9. "Modifications" means any addition to or deletion from the
+ substance or structure of either the Original Code or any previous
+ Modifications. When Covered Code is released as a series of files, a
+ Modification is:
+ A. Any addition to or deletion from the contents of a file
+ containing Original Code or previous Modifications.
+
+ B. Any new file that contains any part of the Original Code or
+ previous Modifications.
+
+ 1.10. "Original Code" means Source Code of computer software code
+ which is described in the Source Code notice required by Exhibit A as
+ Original Code, and which, at the time of its release under this
+ License is not already Covered Code governed by this License.
+
+ 1.10.1. "Patent Claims" means any patent claim(s), now owned or
+ hereafter acquired, including without limitation, method, process,
+ and apparatus claims, in any patent Licensable by grantor.
+
+ 1.11. "Source Code" means the preferred form of the Covered Code for
+ making modifications to it, including all modules it contains, plus
+ any associated interface definition files, scripts used to control
+ compilation and installation of an Executable, or source code
+ differential comparisons against either the Original Code or another
+ well known, available Covered Code of the Contributor's choice. The
+ Source Code can be in a compressed or archival form, provided the
+ appropriate decompression or de-archiving software is widely available
+ for no charge.
+
+ 1.12. "You" (or "Your") means an individual or a legal entity
+ exercising rights under, and complying with all of the terms of, this
+ License or a future version of this License issued under Section 6.1.
+ For legal entities, "You" includes any entity which controls, is
+ controlled by, or is under common control with You. For purposes of
+ this definition, "control" means (a) the power, direct or indirect,
+ to cause the direction or management of such entity, whether by
+ contract or otherwise, or (b) ownership of more than fifty percent
+ (50%) of the outstanding shares or beneficial ownership of such
+ entity.
+
+2. Source Code License.
+
+ 2.1. The Initial Developer Grant.
+ The Initial Developer hereby grants You a world-wide, royalty-free,
+ non-exclusive license, subject to third party intellectual property
+ claims:
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by Initial Developer to use, reproduce,
+ modify, display, perform, sublicense and distribute the Original
+ Code (or portions thereof) with or without Modifications, and/or
+ as part of a Larger Work; and
+
+ (b) under Patents Claims infringed by the making, using or
+ selling of Original Code, to make, have made, use, practice,
+ sell, and offer for sale, and/or otherwise dispose of the
+ Original Code (or portions thereof).
+
+ (c) the licenses granted in this Section 2.1(a) and (b) are
+ effective on the date Initial Developer first distributes
+ Original Code under the terms of this License.
+
+ (d) Notwithstanding Section 2.1(b) above, no patent license is
+ granted: 1) for code that You delete from the Original Code; 2)
+ separate from the Original Code; or 3) for infringements caused
+ by: i) the modification of the Original Code or ii) the
+ combination of the Original Code with other software or devices.
+
+ 2.2. Contributor Grant.
+ Subject to third party intellectual property claims, each Contributor
+ hereby grants You a world-wide, royalty-free, non-exclusive license
+
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by Contributor, to use, reproduce, modify,
+ display, perform, sublicense and distribute the Modifications
+ created by such Contributor (or portions thereof) either on an
+ unmodified basis, with other Modifications, as Covered Code
+ and/or as part of a Larger Work; and
+
+ (b) under Patent Claims infringed by the making, using, or
+ selling of Modifications made by that Contributor either alone
+ and/or in combination with its Contributor Version (or portions
+ of such combination), to make, use, sell, offer for sale, have
+ made, and/or otherwise dispose of: 1) Modifications made by that
+ Contributor (or portions thereof); and 2) the combination of
+ Modifications made by that Contributor with its Contributor
+ Version (or portions of such combination).
+
+ (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
+ effective on the date Contributor first makes Commercial Use of
+ the Covered Code.
+
+ (d) Notwithstanding Section 2.2(b) above, no patent license is
+ granted: 1) for any code that Contributor has deleted from the
+ Contributor Version; 2) separate from the Contributor Version;
+ 3) for infringements caused by: i) third party modifications of
+ Contributor Version or ii) the combination of Modifications made
+ by that Contributor with other software (except as part of the
+ Contributor Version) or other devices; or 4) under Patent Claims
+ infringed by Covered Code in the absence of Modifications made by
+ that Contributor.
+
+3. Distribution Obligations.
+
+ 3.1. Application of License.
+ The Modifications which You create or to which You contribute are
+ governed by the terms of this License, including without limitation
+ Section 2.2. The Source Code version of Covered Code may be
+ distributed only under the terms of this License or a future version
+ of this License released under Section 6.1, and You must include a
+ copy of this License with every copy of the Source Code You
+ distribute. You may not offer or impose any terms on any Source Code
+ version that alters or restricts the applicable version of this
+ License or the recipients' rights hereunder. However, You may include
+ an additional document offering the additional rights described in
+ Section 3.5.
+
+ 3.2. Availability of Source Code.
+ Any Modification which You create or to which You contribute must be
+ made available in Source Code form under the terms of this License
+ either on the same media as an Executable version or via an accepted
+ Electronic Distribution Mechanism to anyone to whom you made an
+ Executable version available; and if made available via Electronic
+ Distribution Mechanism, must remain available for at least twelve (12)
+ months after the date it initially became available, or at least six
+ (6) months after a subsequent version of that particular Modification
+ has been made available to such recipients. You are responsible for
+ ensuring that the Source Code version remains available even if the
+ Electronic Distribution Mechanism is maintained by a third party.
+
+ 3.3. Description of Modifications.
+ You must cause all Covered Code to which You contribute to contain a
+ file documenting the changes You made to create that Covered Code and
+ the date of any change. You must include a prominent statement that
+ the Modification is derived, directly or indirectly, from Original
+ Code provided by the Initial Developer and including the name of the
+ Initial Developer in (a) the Source Code, and (b) in any notice in an
+ Executable version or related documentation in which You describe the
+ origin or ownership of the Covered Code.
+
+ 3.4. Intellectual Property Matters
+ (a) Third Party Claims.
+ If Contributor has knowledge that a license under a third party's
+ intellectual property rights is required to exercise the rights
+ granted by such Contributor under Sections 2.1 or 2.2,
+ Contributor must include a text file with the Source Code
+ distribution titled "LEGAL" which describes the claim and the
+ party making the claim in sufficient detail that a recipient will
+ know whom to contact. If Contributor obtains such knowledge after
+ the Modification is made available as described in Section 3.2,
+ Contributor shall promptly modify the LEGAL file in all copies
+ Contributor makes available thereafter and shall take other steps
+ (such as notifying appropriate mailing lists or newsgroups)
+ reasonably calculated to inform those who received the Covered
+ Code that new knowledge has been obtained.
+
+ (b) Contributor APIs.
+ If Contributor's Modifications include an application programming
+ interface and Contributor has knowledge of patent licenses which
+ are reasonably necessary to implement that API, Contributor must
+ also include this information in the LEGAL file.
+
+ (c) Representations.
+ Contributor represents that, except as disclosed pursuant to
+ Section 3.4(a) above, Contributor believes that Contributor's
+ Modifications are Contributor's original creation(s) and/or
+ Contributor has sufficient rights to grant the rights conveyed by
+ this License.
+
+ 3.5. Required Notices.
+ You must duplicate the notice in Exhibit A in each file of the Source
+ Code. If it is not possible to put such notice in a particular Source
+ Code file due to its structure, then You must include such notice in a
+ location (such as a relevant directory) where a user would be likely
+ to look for such a notice. If You created one or more Modification(s)
+ You may add your name as a Contributor to the notice described in
+ Exhibit A. You must also duplicate this License in any documentation
+ for the Source Code where You describe recipients' rights or ownership
+ rights relating to Covered Code. You may choose to offer, and to
+ charge a fee for, warranty, support, indemnity or liability
+ obligations to one or more recipients of Covered Code. However, You
+ may do so only on Your own behalf, and not on behalf of the Initial
+ Developer or any Contributor. You must make it absolutely clear than
+ any such warranty, support, indemnity or liability obligation is
+ offered by You alone, and You hereby agree to indemnify the Initial
+ Developer and every Contributor for any liability incurred by the
+ Initial Developer or such Contributor as a result of warranty,
+ support, indemnity or liability terms You offer.
+
+ 3.6. Distribution of Executable Versions.
+ You may distribute Covered Code in Executable form only if the
+ requirements of Section 3.1-3.5 have been met for that Covered Code,
+ and if You include a notice stating that the Source Code version of
+ the Covered Code is available under the terms of this License,
+ including a description of how and where You have fulfilled the
+ obligations of Section 3.2. The notice must be conspicuously included
+ in any notice in an Executable version, related documentation or
+ collateral in which You describe recipients' rights relating to the
+ Covered Code. You may distribute the Executable version of Covered
+ Code or ownership rights under a license of Your choice, which may
+ contain terms different from this License, provided that You are in
+ compliance with the terms of this License and that the license for the
+ Executable version does not attempt to limit or alter the recipient's
+ rights in the Source Code version from the rights set forth in this
+ License. If You distribute the Executable version under a different
+ license You must make it absolutely clear that any terms which differ
+ from this License are offered by You alone, not by the Initial
+ Developer or any Contributor. You hereby agree to indemnify the
+ Initial Developer and every Contributor for any liability incurred by
+ the Initial Developer or such Contributor as a result of any such
+ terms You offer.
+
+ 3.7. Larger Works.
+ You may create a Larger Work by combining Covered Code with other code
+ not governed by the terms of this License and distribute the Larger
+ Work as a single product. In such a case, You must make sure the
+ requirements of this License are fulfilled for the Covered Code.
+
+4. Inability to Comply Due to Statute or Regulation.
+
+ If it is impossible for You to comply with any of the terms of this
+ License with respect to some or all of the Covered Code due to
+ statute, judicial order, or regulation then You must: (a) comply with
+ the terms of this License to the maximum extent possible; and (b)
+ describe the limitations and the code they affect. Such description
+ must be included in the LEGAL file described in Section 3.4 and must
+ be included with all distributions of the Source Code. Except to the
+ extent prohibited by statute or regulation, such description must be
+ sufficiently detailed for a recipient of ordinary skill to be able to
+ understand it.
+
+5. Application of this License.
+
+ This License applies to code to which the Initial Developer has
+ attached the notice in Exhibit A and to related Covered Code.
+
+6. Versions of the License.
+
+ 6.1. New Versions.
+ Netscape Communications Corporation ("Netscape") may publish revised
+ and/or new versions of the License from time to time. Each version
+ will be given a distinguishing version number.
+
+ 6.2. Effect of New Versions.
+ Once Covered Code has been published under a particular version of the
+ License, You may always continue to use it under the terms of that
+ version. You may also choose to use such Covered Code under the terms
+ of any subsequent version of the License published by Netscape. No one
+ other than Netscape has the right to modify the terms applicable to
+ Covered Code created under this License.
+
+ 6.3. Derivative Works.
+ If You create or use a modified version of this License (which you may
+ only do in order to apply it to code which is not already Covered Code
+ governed by this License), You must (a) rename Your license so that
+ the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
+ "MPL", "NPL" or any confusingly similar phrase do not appear in your
+ license (except to note that your license differs from this License)
+ and (b) otherwise make it clear that Your version of the license
+ contains terms which differ from the Mozilla Public License and
+ Netscape Public License. (Filling in the name of the Initial
+ Developer, Original Code or Contributor in the notice described in
+ Exhibit A shall not of themselves be deemed to be modifications of
+ this License.)
+
+7. DISCLAIMER OF WARRANTY.
+
+ COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+ WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
+ DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+ THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
+ IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
+ YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
+ COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
+ OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+ ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+8. TERMINATION.
+
+ 8.1. This License and the rights granted hereunder will terminate
+ automatically if You fail to comply with terms herein and fail to cure
+ such breach within 30 days of becoming aware of the breach. All
+ sublicenses to the Covered Code which are properly granted shall
+ survive any termination of this License. Provisions which, by their
+ nature, must remain in effect beyond the termination of this License
+ shall survive.
+
+ 8.2. If You initiate litigation by asserting a patent infringement
+ claim (excluding declatory judgment actions) against Initial Developer
+ or a Contributor (the Initial Developer or Contributor against whom
+ You file such action is referred to as "Participant") alleging that:
+
+ (a) such Participant's Contributor Version directly or indirectly
+ infringes any patent, then any and all rights granted by such
+ Participant to You under Sections 2.1 and/or 2.2 of this License
+ shall, upon 60 days notice from Participant terminate prospectively,
+ unless if within 60 days after receipt of notice You either: (i)
+ agree in writing to pay Participant a mutually agreeable reasonable
+ royalty for Your past and future use of Modifications made by such
+ Participant, or (ii) withdraw Your litigation claim with respect to
+ the Contributor Version against such Participant. If within 60 days
+ of notice, a reasonable royalty and payment arrangement are not
+ mutually agreed upon in writing by the parties or the litigation claim
+ is not withdrawn, the rights granted by Participant to You under
+ Sections 2.1 and/or 2.2 automatically terminate at the expiration of
+ the 60 day notice period specified above.
+
+ (b) any software, hardware, or device, other than such Participant's
+ Contributor Version, directly or indirectly infringes any patent, then
+ any rights granted to You by such Participant under Sections 2.1(b)
+ and 2.2(b) are revoked effective as of the date You first made, used,
+ sold, distributed, or had made, Modifications made by that
+ Participant.
+
+ 8.3. If You assert a patent infringement claim against Participant
+ alleging that such Participant's Contributor Version directly or
+ indirectly infringes any patent where such claim is resolved (such as
+ by license or settlement) prior to the initiation of patent
+ infringement litigation, then the reasonable value of the licenses
+ granted by such Participant under Sections 2.1 or 2.2 shall be taken
+ into account in determining the amount or value of any payment or
+ license.
+
+ 8.4. In the event of termination under Sections 8.1 or 8.2 above,
+ all end user license agreements (excluding distributors and resellers)
+ which have been validly granted by You or any distributor hereunder
+ prior to termination shall survive termination.
+
+9. LIMITATION OF LIABILITY.
+
+ UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+ (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+ DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
+ OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
+ ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
+ CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
+ WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+ COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+ INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+ LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+ RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+ PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
+ EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
+ THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+10. U.S. GOVERNMENT END USERS.
+
+ The Covered Code is a "commercial item," as that term is defined in
+ 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+ software" and "commercial computer software documentation," as such
+ terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
+ C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
+ all U.S. Government End Users acquire Covered Code with only those
+ rights set forth herein.
+
+11. MISCELLANEOUS.
+
+ This License represents the complete agreement concerning subject
+ matter hereof. If any provision of this License is held to be
+ unenforceable, such provision shall be reformed only to the extent
+ necessary to make it enforceable. This License shall be governed by
+ California law provisions (except to the extent applicable law, if
+ any, provides otherwise), excluding its conflict-of-law provisions.
+ With respect to disputes in which at least one party is a citizen of,
+ or an entity chartered or registered to do business in the United
+ States of America, any litigation relating to this License shall be
+ subject to the jurisdiction of the Federal Courts of the Northern
+ District of California, with venue lying in Santa Clara County,
+ California, with the losing party responsible for costs, including
+ without limitation, court costs and reasonable attorneys' fees and
+ expenses. The application of the United Nations Convention on
+ Contracts for the International Sale of Goods is expressly excluded.
+ Any law or regulation which provides that the language of a contract
+ shall be construed against the drafter shall not apply to this
+ License.
+
+12. RESPONSIBILITY FOR CLAIMS.
+
+ As between Initial Developer and the Contributors, each party is
+ responsible for claims and damages arising, directly or indirectly,
+ out of its utilization of rights under this License and You agree to
+ work with Initial Developer and Contributors to distribute such
+ responsibility on an equitable basis. Nothing herein is intended or
+ shall be deemed to constitute any admission of liability.
+
+13. MULTIPLE-LICENSED CODE.
+
+ Initial Developer may designate portions of the Covered Code as
+ "Multiple-Licensed". "Multiple-Licensed" means that the Initial
+ Developer permits you to utilize portions of the Covered Code under
+ Your choice of the NPL or the alternative licenses, if any, specified
+ by the Initial Developer in the file described in Exhibit A.
+
+EXHIBIT A -Mozilla Public License.
+
+ ``The contents of this file are subject to the Mozilla Public License
+ Version 1.1 (the "License"); you may not use this file except in
+ compliance with the License. You may obtain a copy of the License at
+ http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ License for the specific language governing rights and limitations
+ under the License.
+
+ The Original Code is ______________________________________.
+
+ The Initial Developer of the Original Code is ________________________.
+ Portions created by ______________________ are Copyright (C) ______
+ _______________________. All Rights Reserved.
+
+ Contributor(s): ______________________________________.
+
+ Alternatively, the contents of this file may be used under the terms
+ of the _____ license (the "[___] License"), in which case the
+ provisions of [______] License are applicable instead of those
+ above. If you wish to allow use of your version of this file only
+ under the terms of the [____] License and not to allow others to use
+ your version of this file under the MPL, indicate your decision by
+ deleting the provisions above and replace them with the notice and
+ other provisions required by the [___] License. If you do not delete
+ the provisions above, a recipient may use your version of this file
+ under either the MPL or the [___] License."
+
+ [NOTE: The text of this Exhibit A may differ slightly from the text of
+ the notices in the Source Code files of the Original Code. You should
+ use the text of this Exhibit A rather than the text found in the
+ Original Code Source Code for Your Modifications.]
+
diff --git a/upstream/org.ibex.core/Makefile b/upstream/org.ibex.core/Makefile
new file mode 100644
index 0000000..2c8b68a
--- /dev/null
+++ b/upstream/org.ibex.core/Makefile
@@ -0,0 +1,439 @@
+#############################################################################
+#
+# The Ibex Makefile
+#
+
+# auto directory creation with %/%: %?
+
+ifeq ($(platform),)
+# default
+platform := Java2
+endif
+
+target_Darwin := powerpc-apple-darwin
+target_Win32 := i686-pc-mingw32
+target_Solaris := sparc-sun-solaris2.7
+target_Linux := i686-pc-linux-gnu
+target := $(target_$(platform))
+
+all: Java2 Linux Win32 Darwin Solaris
+
+clean: ; rm -rf build
+dist-clean:
+ rm -rf .configure* .install* build .compile .build*
+ find upstream -name config.cache -exec rm -f {} \;
+ test -e upstream/org.ibex.nestedvm && make -C upstream/org.ibex.nestedvm clean
+ rm -f .install_org.ibex.nestedvm
+
+libwing_Linux := -Lupstream/install/i686-pc-linux-gnu/lib/
+libwing_Linux += upstream/install/i686-pc-linux-gnu/lib/libWINGs.a
+libwing_Linux += upstream/install/i686-pc-linux-gnu/lib/libwraster.a
+
+libwing_Solaris := -Lupstream/install/sparc-sun-solaris2.7/lib/
+libwing_Solaris += upstream/install/sparc-sun-solaris2.7/lib/libWINGs.a
+libwing_Solaris += upstream/install/sparc-sun-solaris2.7/lib/libwraster.a
+
+Java2: build/Java2/ibex.jar
+JVM:; @make Java2
+Linux: ; @make gcj platform=Linux link_flags="$(libwing_Linux) -lXpm -lX11 -lXext"
+Solaris: ; @make gcj platform=Solaris link_flags="$(libwing_Solaris) -lXpm -lX11 -lXext -lpthread"
+Win32: ; @make gcj platform=Win32 link_flags="-Wl,--subsystem,windows -lcomdlg32"
+Darwin: ; @make gcj platform=Darwin link_flags="$(darwin_linker_flags)"
+
+libjava_dir := $(shell pwd)/upstream/gcc-3.3/build-$(target)/$(target)/libjava
+darwin_libdir := upstream/install/powerpc-apple-darwin/lib
+darwin_ogl_libdir := /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries
+
+target_bin_extension_$(platform) := $(shell echo $(platform) | tr A-Z a-z)
+target_bin_extension_Win32 := exe
+target_bin_extension_Java2 := jar
+target_bin_extension := $(target_bin_extension_$(platform))
+target_bin := ibex.$(target_bin_extension)
+
+ifeq ($(platform),Darwin)
+gcc_optimizations := -O0
+else
+gcc_optimizations := -Os
+endif
+#gcc_optimizations := -O9 -ffast-math -fomit-frame-pointer -foptimize-sibling-calls
+#gcc_optimizations += -finline-functions -funroll-loops -ffunction-sections -fdata-sections
+gcc_flags := -nostdinc $(gcc_optimizations) -Ibuild/h -Iupstream/jpeg-6b/src -Iupstream/jpeg-6b/build-$(target) -g
+gcc_flags += -Iupstream/install/lib/gcc-lib/$(target)/3.3/include -Iupstream/install/$(target)/include
+#gcc_flags += -ffunction-sections -fdata-sections -fno-omit-frame-pointer
+gcj := CLASSPATH=build/java upstream/install/bin/$(target)-gcj $(gcc_flags)
+ifneq ($(platform),Darwin)
+gcj += -finhibit-reflection
+endif
+#gcj += -fassume-compiled -fmerge-all-constants
+#gcj += -foptimize-static-class-initialization -feliminate-dwarf2-dups -w
+gcjh := $(shell pwd)/upstream/install/bin/$(shell test -e upstream/install/bin/$(target)-gcjh && echo $(target)-)gcjh
+g++ := upstream/install/bin/$(target)-gcj $(gcc_flags) -Iupstream/install/include -Wno-multichar
+gcc := upstream/install/bin/$(target)-gcc $(gcc_flags)
+nm := upstream/install/$(target)/bin/nm
+jar := $(shell ((type fastjar &>/dev/null) && echo fastjar) || echo jar)
+ifeq ($(platform),Java2)
+javac := javac -classpath upstream/bcel-5.1/src/bcel-5.1.jar -d build/class/ -sourcepath build/java/
+else
+javac := $(gcj) -fCLASSPATH=upstream/bcel-5.1/src/bcel-5.1.jar -d build/class/ -Ibuild/java -Iupstream/gnu.regexp-1.1.4/src/src -C
+endif
+
+gcj: .install_gcc-3.3_$(target)
+ @make build/$(platform)/$(target_bin) link_flags="$(link_flags)" platform=$(platform)
+
+include Makefile.upstream
+
+# find the superclasses of a platform class
+#superclass_%:
+# @if [ "$*" = "org.ibex.plat.Darwin" ]; then echo org.ibex.plat.OpenGL; fi
+# @echo $*
+# @grep -s extends src/$(subst .,/,$*).java | sed s_.\*extends\ __ | head -n 1 | sed s_\ .\*__ |\
+# xargs --replace make -s superclass_org.ibex.plat.{}
+
+plat_classes_Platform := org.ibex.plat.Platform
+plat_classes_AWT := org.ibex.plat.AWT $(plat_classes_Platform)
+plat_classes_Java2 := org.ibex.plat.Java2 org.ibex.plat.JVM $(plat_classes_AWT)
+plat_classes_GCJ := org.ibex.plat.GCJ $(plat_classes_Platform)
+plat_classes_Win32 := org.ibex.plat.Win32 $(plat_classes_GCJ)
+plat_classes_POSIX := org.ibex.plat.POSIX $(plat_classes_GCJ)
+plat_classes_X11 := org.ibex.plat.X11 $(plat_classes_POSIX)
+plat_classes_Linux := org.ibex.plat.Linux $(plat_classes_X11)
+plat_classes_Solaris := org.ibex.plat.Solaris $(plat_classes_X11)
+plat_classes_Darwin := org.ibex.plat.Darwin org.ibex.plat.OpenGL $(plat_classes_POSIX)
+
+plat_java_src_sources := $(patsubst %,build/java/%.java,$(subst .,/,$(plat_classes_$(platform))))
+java_src_sources := $(shell find src -name '*.java' | grep -v NanoGoat | grep -v Preprocessor | grep -v /plat/)
+java_src_sources += $(plat_java_src_sources)
+java_sources := $(patsubst src/%.java, build/java/%.java, $(java_src_sources))
+
+java_sources += build/java/org/xwt/mips/util/SeekableByteArray.java
+java_sources += build/java/org/xwt/mips/util/SeekableData.java
+java_sources += build/java/org/xwt/mips/util/SeekableFile.java
+java_sources += build/java/org/xwt/mips/util/SeekableInputStream.java
+java_sources += build/java/org/xwt/mips/Registers.java
+java_sources += build/java/org/xwt/mips/Runtime.java
+java_sources += build/java/org/xwt/mips/UsermodeConstants.java
+
+java_sources += build/java/org/ibex/crypto/DER.java
+java_sources += build/java/org/ibex/crypto/Digest.java
+java_sources += build/java/org/ibex/crypto/HMAC.java
+java_sources += build/java/org/ibex/crypto/MD2.java
+java_sources += build/java/org/ibex/crypto/MD5.java
+java_sources += build/java/org/ibex/crypto/PKCS1.java
+java_sources += build/java/org/ibex/crypto/RC4.java
+java_sources += build/java/org/ibex/crypto/RSA.java
+java_sources += build/java/org/ibex/crypto/SHA1.java
+java_sources += build/java/org/ibex/crypto/X509.java
+java_sources += build/java/org/ibex/crypto/Base64.java
+java_sources += build/java/org/ibex/net/ssl/RootCerts.java
+java_sources += build/java/org/ibex/net/SSL.java
+
+java_sources += build/java/gnu/regexp/CharIndexed.java
+java_sources += build/java/gnu/regexp/CharIndexedCharArray.java
+java_sources += build/java/gnu/regexp/CharIndexedInputStream.java
+java_sources += build/java/gnu/regexp/CharIndexedString.java
+java_sources += build/java/gnu/regexp/CharIndexedStringBuffer.java
+java_sources += build/java/gnu/regexp/RE.java
+java_sources += build/java/gnu/regexp/REException.java
+java_sources += build/java/gnu/regexp/REFilterInputStream.java
+java_sources += build/java/gnu/regexp/REMatch.java
+java_sources += build/java/gnu/regexp/REMatchEnumeration.java
+java_sources += build/java/gnu/regexp/RESyntax.java
+java_sources += build/java/gnu/regexp/REToken.java
+java_sources += build/java/gnu/regexp/RETokenAny.java
+java_sources += build/java/gnu/regexp/RETokenBackRef.java
+java_sources += build/java/gnu/regexp/RETokenChar.java
+java_sources += build/java/gnu/regexp/RETokenEnd.java
+java_sources += build/java/gnu/regexp/RETokenEndSub.java
+java_sources += build/java/gnu/regexp/RETokenLookAhead.java
+java_sources += build/java/gnu/regexp/RETokenWordBoundary.java
+java_sources += build/java/gnu/regexp/RETokenOneOf.java
+java_sources += build/java/gnu/regexp/RETokenPOSIX.java
+java_sources += build/java/gnu/regexp/RETokenRange.java
+java_sources += build/java/gnu/regexp/RETokenRepeated.java
+java_sources += build/java/gnu/regexp/RETokenStart.java
+java_sources += build/java/gnu/regexp/UncheckedRE.java
+java_sources += build/java/gnu/regexp/CharIndexedReader.java
+java_sources += build/java/gnu/regexp/REFilterReader.java
+
+java_classes := $(java_sources:build/java/%.java=build/class/%.class)
+java_classes += build/class/gnu/regexp/CharUnit.class build/class/gnu/regexp/IntPair.class
+java_classes += build/class/org/ibex/util/MIPSApps.class
+
+### Java Source Files ##############################################################################
+
+$(java_sources):: .download_org.ibex.crypto .download_org.ibex.nestedvm .download_gnu.regexp-1.1.4
+link_upstream = build/java/$(1):: $(2)/$(1) $(3); \
+ @echo -e "\033[1mlinking .java -> .java: $$@\033[0m"; \
+ mkdir -p $$(@D); ln -fs `echo $$(@D)/ | sed 's_[^/]*//*_../_g'`/$$< $$@
+build/cc/%.cc: src/%.c ; @echo linking $@; mkdir -p $(@D); ln -fs `echo $(@D)/ | sed 's_[^/]*//*_../_g'`/$< $@
+build/res/%: src/% ; @echo linking $@; mkdir -p $(@D); ln -fs `echo $(@D)/ | sed 's_[^/]*//*_../_g'`/$< $@
+$(call link_upstream,org/xwt/mips/%.java,upstream/org.ibex.nestedvm/src,.download_org.ibex.nestedvm)
+$(call link_upstream,org/ibex/crypto/%.java,upstream/org.ibex.crypto/src,.download_org.ibex.crypto)
+$(call link_upstream,org/ibex/net/ssl/%.java,upstream/org.ibex.crypto/src,.download_org.ibex.crypto)
+$(call link_upstream,org/ibex/net/SSL.java,upstream/org.ibex.crypto/src,.download_org.ibex.crypto)
+$(call link_upstream,gnu/regexp/%.java,upstream/gnu.regexp-1.1.4/src/src)
+build/java/org/ibex/%.java: src/org/ibex/%.java .download_gnu.regexp-1.1.4 build/class/org/ibex/util/Preprocessor.class
+ @echo -e "\033[1mpreprocessing .java -> .java: $<\033[0m"
+ mkdir -p `dirname $@`; java -cp build/class:upstream/gnu.regexp-1.1.4/src/src org.ibex.util.Preprocessor < $< > $@
+$(call link_upstream,%.java,src)
+$(call link_upstream,%.cc,src)
+
+
+### Java Class Files ##############################################################################
+
+build/class/org/ibex/util/Preprocessor.class: src/org/ibex/util/Preprocessor.java
+ @mkdir -p build/class/gnu/regexp build/class/org/ibex/util
+ $(javac) $< src/org/ibex/util/Vec.java upstream/gnu.regexp-1.1.4/src/src/gnu/regexp/*.java
+
+compile: .compile
+.compile: $(java_sources)
+ @echo -e "\n\033[1mcompiling .java -> .class\033[0m"
+ @echo $(java_sources) | tr ' ' '\n' | sed 's_build/java/_ _' | sed 's_/_._g' | sed 's_.java$$__g'
+ @make -s $(java_sources)
+ $(javac) $(java_sources)
+ touch $@
+
+build/class/org/ibex/util/MIPSApps.class: build/mips/mipsapps.mips .install_org.ibex.nestedvm
+ @mkdir -p build/java/org/ibex/util
+ @echo -e "\n\033[1mtranslating .mips -> .class: $<\033[0m"
+ java -cp upstream/org.ibex.nestedvm/build:upstream/org.ibex.nestedvm/upstream/build/bcel-5.1/bcel-5.1.jar \
+ org.xwt.mips.Compiler org.ibex.util.MIPSApps $< -outfile $@
+
+build/Java2/ibex.jar: compile build/res/builtin.jar build/class/org/ibex/util/MIPSApps.class
+ @echo -e "\n\033[1marchiving .class -> .jar: build/Java2/ibex.jar\033[0m"
+ mkdir -p build/Java2
+ echo -e "Manifest-Version: 1.0\nMain-Class: org.ibex.core.Main\n" > build/Java2/.manifest
+ cd build/class/org/ibex; ln -sf ../../../res/builtin.jar
+ cd build/class; $(jar) cfm ../Java2/ibex.jar ../Java2/.manifest `find . -name \*.class -or -name \*.jar`
+
+
+### Headers ##################################################################################
+
+java_headers := $(java_sources:build/java/%.java=build/h/%.h)
+build/h/%.h: build/class/%.class .compile
+ @echo -e "\n\033[1mextracting .class -> .h: $<\033[0m"
+ mkdir -p `dirname $@`
+ ls `echo $< | sed s/.class\$$//`*.class |\
+ sed s_build/class/__ | sed s/.class\$$//g | sed s_/_._g | (cd build/class; xargs $(gcjh) -d ../h --classpath .)
+
+
+### Native Code ##############################################################################
+
+# a hack since we've disabled gcj's awt implementation
+build/$(platform)/org/ibex/plat/Linux.cc.o: .install_WindowMaker-0.80.2_$(target)
+build/$(platform)/org/ibex/plat/Solaris.cc.o: .install_WindowMaker-0.80.2_$(target)
+
+nat_libjava_files_ := boehm.o exception.o posix-threads.o posix.o prims.o resolve.o java/net/natInetAddress.o
+nat_libjava_files_ += java/net/natPlainSocketImpl.o java/util/zip/nat*.o gnu/gcj/runtime/natFirstThread.o
+nat_libjava_files_ += gnu/gcj/runtime/natNameFinder.o gnu/gcj/runtime/natStackTrace.o gnu/gcj/runtime/natSharedLibLoader.o
+nat_libjava_files_ += gnu/gcj/runtime/natStringBuffer.o gnu/gcj/runtime/natVMClassLoader.o gnu/gcj/runtime/natFinalizerThread.o
+nat_libjava_files_ += $(shell cd $(libjava_dir) 2>/dev/null; find java/lang -name \*.o -not -name '*[A-Z]*' 2>/dev/null)
+nat_libjava_files_ += $(shell cd $(libjava_dir) 2>/dev/null; find java/lang -name nat\*.o 2>/dev/null)
+nat_libjava_files := $(nat_libjava_files_:%=$(libjava_dir)/%)
+nat_libjava_files += $(libjava_dir)/java/io/natFile.o $(libjava_dir)/java/io/natFileDescriptor.o
+
+build/$(platform)/org/ibex/plat/$(platform).cc.o: src/org/ibex/plat/$(platform).cc src/org/ibex/plat/*.cc .configure_jpeg-6b_$(target)
+ @make $(java_headers)
+ @echo -e "\n\033[1mcompiling .cc -> .o: $<\033[0m"
+ mkdir -p `dirname $@`
+ $(g++) -Iupstream/gcc-3.3/build-$(target)/$(target)/libjava -c $< -o $@
+
+
+bcel_jar := upstream/bcel-5.1/src/bcel-5.1.jar
+build/$(platform)/ibex.pruned.jar: .compile $(java_classes) .install_jpeg-6b_$(target) build/$(platform)/builtin.o build/$(platform)/org/ibex/plat/$(platform).cc.o
+ @echo -e "\n\033[1mpruning .jar -> .jar\033[0m"
+ifneq ($(platform),Darwin)
+ cp upstream/install/share/java/libgcj-3.3.jar build/$(platform)/ibex.jar
+ cd build/class; jar uf ../$(platform)/ibex.jar $(java_classes:build/class/%.class=%*.class)
+ ($(nm) $(nat_libjava_files) build/$(platform)/org/ibex/plat/$(platform).cc.o; echo org.ibex.plat.$(platform).main) |\
+ grep _ZN | c++filt --format java | grep " U " | sed 's_ * U __' | sed 's_(.*__' \
+ | java -cp $(bcel_jar):build/class org.ibex.util.NanoGoat build/$(platform)/ibex.jar | tee nanogoat.log
+ mkdir tmp; cd tmp; fastjar xvf ../build/$(platform)/ibex.jar.pruned; rm gnu/gcj/runtime/FirstThread*; cp ../build/class/org/ibex/plat/GCJ* org/ibex/plat/; cp -r ../build/class/org/xwt/mips/* org/xwt/mips/; mkdir -p $(shell pwd)/build/$(platform)/java/lang; mv java/lang/Class.class $(shell pwd)/build/$(platform)/java/lang; fastjar cvf ../build/$(platform)/ibex.jar.pruned .; cd ..; rm -rf tmp
+ mv build/$(platform)/ibex.jar.pruned $@
+endif
+ifeq ($(platform),Darwin)
+ cd build/class; jar cf ../$(platform)/ibex.pruned.jar \
+ org/ibex/plat/Platform*.class \
+ $(filter-out org/ibex/util/NanoGoat%,$(java_classes:build/class/%.class=%*.class))
+endif
+
+ifeq ($(platform),Win32)
+filedes := $(shell pwd)/upstream/gcc-3.3/src/libjava/java/io/natFileWin32.cc
+filedes += $(shell pwd)/upstream/gcc-3.3/src/libjava/java/io/natFileDescriptorWin32.cc
+else
+filedes := $(shell pwd)/upstream/gcc-3.3/src/libjava/java/io/natFilePosix.cc
+filedes += $(shell pwd)/upstream/gcc-3.3/src/libjava/java/io/natFileDescriptorPosix.cc
+endif
+
+final_compile := --main=org.ibex.plat.$(platform)
+final_compile += -Dfile.encoding=UTF8
+final_compile += -DPREFIX=\"\"
+final_compile += -Lupstream/install/$(target)/lib -Lupstream/install/lib
+final_compile += -g -save-temps -w -v
+final_compile += -Iupstream/jpeg-6b/build-$(target)/
+final_compile += -Iupstream/jpeg-6b/src
+final_compile += -Ibuild/h
+final_compile += -Iupstream/gcc-3.3/src/gcc
+final_compile += -Iupstream/gcc-3.3/build-$(target)/$(target)/boehm-gc/
+final_compile += -Iupstream/gcc-3.3/build-$(target)/$(target)/boehm-gc/include
+final_compile += -Iupstream/gcc-3.3/src/boehm-gc
+final_compile += -Iupstream/gcc-3.3/src/boehm-gc/include
+final_compile += -Iupstream/gcc-3.3/src/libjava
+final_compile += -Iupstream/gcc-3.3/src/libjava/include
+final_compile += -Iupstream/gcc-3.3/build-$(target)/$(target)/libjava/
+final_compile += -Iupstream/gcc-3.3/build-$(target)/$(target)/libjava/include
+final_compile += -Ufile
+final_compile += src/org/ibex/plat/$(platform).cc
+final_compile += upstream/jpeg-6b/build-$(target)/libjpeg.a
+final_compile += build/$(platform)/builtin.o
+final_compile += build/$(platform)/ibex.pruned.jar
+ifneq ($(platform),Darwin)
+final_compile += $(libjava_dir)/../boehm-gc/.libs/libgcjgc.a
+final_compile += -fno-store-check
+final_compile += -finhibit-reflection
+final_compile += $(nat_libjava_files_:%.o=$(shell pwd)/upstream/gcc-3.3/src/libjava/%.c*)
+final_compile += $(filedes)
+final_compile += build/$(platform)/Class.o
+final_compile += $(libjava_dir)/gnu/gcj/runtime/FirstThread.o
+final_compile += -Wl,-O2,--relax,--gc-sections,--noinhibit-exec,--no-whole-archive
+final_compile += -lz
+else
+final_compile += -Wl,-dylib_file,/usr/lib/libSystem.B.dylib:$(darwin_libdir)/libSystem.B.dylib
+final_compile += -Wl,-dylib_file,/usr/lib/system/libmathCommon.A.dylib:$(darwin_libdir)/libmathCommon.A.dylib
+final_compile += -Wl,-dylib_file,$(darwin_ogl_libdir)/libGL.dylib:$(darwin_libdir)/libGL.dylib
+final_compile += -Wl,-dylib_file,$(darwin_ogl_libdir):$(darwin_libdir)/libGLU.dylib
+final_compile += -lSystem.B -lmathCommon.A -lGL -lGLU
+#final_compile += $(libjava_dir)/.libs/libgcj.a
+#final_compile += $(libjava_dir)/../boehm-gc/.libs/libgcjgc.a
+#final_compile += -lgcc
+#final_compile += -lm
+#final_compile += -lpthread
+#final_compile += -lgcc
+#final_compile += -lcrt1.o
+#final_compile += -lcrt2.o
+#final_compile += -lSystem
+endif
+
+
+ifneq ($(target_bin_extension),jar)
+build/$(platform)/$(target_bin): build/$(platform)/ibex.pruned.jar src/org/ibex/plat/*.cc build/$(platform)/builtin.o $(java_headers)
+
+ @echo -e "\n\033[1mlinking .jar -> $(target_bin).phat\033[0m"
+ifneq ($(platform),Darwin)
+# -Os
+ cd build/$(platform); $(shell pwd)/upstream/install/bin/$(target)-gcj \
+ -fCLASSPATH=$(shell pwd)/upstream/install/share/java/libgcj-3.3.jar \
+ -finhibit-reflection \
+ -fno-store-check \
+ -c java/lang/Class.class
+ cd upstream/gcc-3.3/src/libjava/java/lang;\
+ ln -sf ../../../../build-$(target)/$(target)/libjava/java/lang/natConcreteProcess.cc
+endif
+# we need -fno-store-check since we can't dynamically resolve references to array classes
+ PATH=upstream/install/bin:$$PATH upstream/install/bin/$(target)-gcj $(final_compile) -o $@.phat
+
+ifneq ($(platform),Darwin)
+ @echo; ls -l $@.phat
+
+ @echo -e "\n\033[1mstripping $(target_bin).phat -> $(target_bin)\033[0m"
+ strip $@.phat -o $@
+ @echo; ls -l $@.phat
+
+ @echo -e "\n\033[1mcompressing $(target_bin) -> $(target_bin)\033[0m"
+ upx-ucl-beta $@
+ @echo; ls -l $@.phat
+else
+ mv $@.phat $@
+endif
+endif
+
+### Builtin Resources ##############################################################################
+
+builtin_src := $(shell find src/org/ibex/core/builtin -name '*.*' \! -name '*.xcf')
+build/res/fonts/vera: .download_vera-1.10
+ mkdir -p build/res/fonts/vera
+ cd build/res/fonts/vera; ln -fs ../../../../upstream/vera-1.10/ttf-bitstream-vera-1.10/Vera.ttf
+ cd build/res/fonts/vera; ln -fs ../../../../upstream/vera-1.10/ttf-bitstream-vera-1.10/VeraMono.ttf
+ cd build/res/fonts/vera; ln -fs ../../../../upstream/vera-1.10/ttf-bitstream-vera-1.10/VeraSe.ttf
+build/res/builtin.jar: $(builtin_src:src/%=build/res/%) build/res/fonts/vera
+ @echo -e "\n\033[1mzipping res/* -> .jar: builtin.jar\033[0m"
+ cd build/res; $(jar) cf builtin.jar org/ibex/core/builtin/scar.png fonts
+build/$(platform)/builtin.o: build/res/builtin.jar
+ @echo -e "\n\033[1mwrapping .jar -> .o: resources.o\033[0m"
+ @mkdir -p $(@D)
+ @(echo "unsigned int builtin_length = "; \
+ (wc -c build/res/builtin.jar | sed "s_build.*__"); \
+ echo \;; \
+ echo "unsigned char builtin_bytes[] = {"; \
+ hexdump -ve '"0x" 1/1 "%x,\n"' build/res/builtin.jar; \
+ echo "};") > .builtin.c
+ $(gcc) -c .builtin.c -o build/$(platform)/builtin.o
+
+build/mips/%.c.o: src/%.c .download_libmspack-20030726 .install_org.ibex.nestedvm
+ make .install_freetype-2.1.4_mips-unknown-elf target=mips-unknown-elf
+ make .install_libmspack-20030726_mips-unknown-elf target=mips-unknown-elf
+ mkdir -p $(@D)
+ echo -e "\n\033[1mcompiling $< -> $@ (mips)\033[0m"
+ upstream/install/bin/mips-unknown-elf-gcc -march=r3000 \
+ -Iupstream/freetype-2.1.4/src/include \
+ -Iupstream/libmspack-20030726/src/mspack \
+ -c -o $@ $<
+
+build/mips/mipsapps.mips: build/mips/org/ibex/graphics/Freetype.c.o build/mips/org/ibex/util/MSPack.c.o
+ make .install_freetype-2.1.4_mips-unknown-elf target=mips-unknown-elf
+ make .install_libmspack-20030726_mips-unknown-elf target=mips-unknown-elf
+ @echo -e "\n\033[1mlinking .o -> .mips: $@\033[0m"
+ mkdir -p build/mips build/res
+ upstream/install/bin/mips-unknown-elf-gcc \
+ --static \
+ -march=mips1 \
+ -T upstream/org.ibex.nestedvm/src/org/xwt/mips/linker.ld \
+ -L upstream/org.ibex.nestedvm/build/org/xwt/mips/ \
+ -L upstream/freetype-2.1.4/src/objs \
+ -L upstream/libmspack-20030726/build-mips-unknown-elf/ \
+ -o $@ \
+ $^ \
+ -lfreetype \
+ -lmspack \
+ -Wl,--gc-sections
+
+
+### Maintainer ######################################################################################
+
+current_build := $(shell cat next.build)
+strip_$(platform) := upstream/install/$(target)/bin/strip build/$(platform)/$(target_bin) -o
+strip_Java2 := cp build/$(platform)/$(target_bin)
+install-dist:; $(strip_$(platform)) /var/www/org/xwt/dist/master/ibex-$(current_build).$(target_bin_extension).unsigned
+dist: compile
+ (echo -n 0000; (echo "10k16o16i"; cat next.build | tr a-z A-Z; echo "1+f") | dc) | tail --bytes=5 > next.build-
+ mv next.build- next.build
+ echo -n "Next build will be "; cat next.build
+ nice -n 19 make all
+ make install-dist platform=Win32
+ make install-dist platform=Linux
+ make install-dist platform=Solaris
+ make install-dist platform=Darwin
+ make install-dist platform=Java2
+ echo -e "\n\n\n*** DONE ******************************************"
+
+propose-patch:
+ @darcs send --edit-description -o .darcspatch -T patches@xwt.org http://core.ibex.org/
+ @(echo "To: patches@xwt.org";\
+ SUB=`grep -A10000 New.patches .darcspatch | grep '^\\[' | cut -b 2- | tr \\\n , | sed s_,_,\ _g | sed "s_, *\\$$__"`;\
+ echo "Subject: $$SUB";\
+ echo;\
+ cat .darcspatch) | /usr/sbin/sendmail -bm -f $(USER)@xwt.org patches@xwt.org;
+ @rm .darcspatch
+
+commit: propose-patch
+ darcs push xwt@xwt.org:/var/www/org/ibex/core/
+
+upstream/org.ibex.doc/src/org/ibex/doc/Doc.java: .download_org.ibex.doc
+build/class/org/ibex/doc/Doc.class: upstream/org.ibex.doc/src/org/ibex/doc/Doc.java
+ $(javac) upstream/org.ibex.doc/src/org/ibex/doc/Doc.java
+doc/%.pdf: build/class/org/ibex/util/XML.class build/class/org/ibex/doc/Doc.class
+ cd doc/$*; java -cp ../../build/class org.ibex.doc.Doc < $*.xml > $*.tex
+ cd doc/$*; pdflatex $*.tex
+ mv doc/$*/$*.pdf doc/$*.pdf
+ test `uname` == Darwin && open doc/$*.pdf
+reference: ; rm -f doc/reference.pdf; make doc/reference.pdf
\ No newline at end of file
diff --git a/upstream/org.ibex.core/Makefile.upstream b/upstream/org.ibex.core/Makefile.upstream
new file mode 100644
index 0000000..0749813
--- /dev/null
+++ b/upstream/org.ibex.core/Makefile.upstream
@@ -0,0 +1,217 @@
+#############################################################################
+#
+# The Ibex upstream code Makefile
+#
+
+# deal with Apple's brokenness
+setcc := $(shell test `uname` = Darwin && echo "CC=\"/usr/bin/gcc3 -no-cpp-precomp\"")
+
+# where to get stuff
+url_binutils-2.13.2.1 := ftp://ftp.gnu.org/gnu/binutils/binutils-2.13.2.1.tar.gz
+url_w32api-2.3 := http://unc.dl.sourceforge.net/sourceforge/mingw/w32api-2.3.tar.gz
+url_mingw-runtime-3.0 := http://unc.dl.sourceforge.net/sourceforge/mingw/mingw-runtime-3.0.tar.gz
+url_freetype-2.1.4 := http://unc.dl.sourceforge.net/sourceforge/freetype/freetype-2.1.4.tar.gz
+url_gcc-3.3 := http://mirrors.rcn.net/pub/sourceware/gcc/releases/gcc-3.3/gcc-3.3.tar.gz
+url_jpeg-6b := http://www.ijg.org/files/jpegsrc.v6b.tar.gz
+url_libmspack-20030726 := http://www.kyz.uklinux.net/downloads/libmspack-20030726.tar.gz
+url_vera-1.10 := http://fgo-temp.acc.umu.se/pub/GNOME/sources/ttf-bitstream-vera/1.10/ttf-bitstream-vera-1.10.tar.gz
+url_WindowMaker-0.80.2 := http://windowmaker.org/pub/source/release/WindowMaker-0.80.2.tar.gz
+url_bcel-5.1 := http://www.apache.org/dist/jakarta/bcel/binaries/bcel-5.1.tar.gz
+url_gnu.regexp-1.1.4 := ftp://ftp.tralfamadore.com/pub/java/gnu.regexp-1.1.4.tar.gz
+
+.install_binutils-2.13.2.1_powerpc-apple-darwin: .vendor
+ rm -rf upstream/darwin-linker/src
+ cd upstream/darwin-linker; tar xvzf ../install/powerpc-apple-darwin/cctools-478.tgz
+ifneq ($(shell uname),Darwin)
+ cd upstream/darwin-linker/src/cctools; for A in ../../patches/*.patch; do patch -p0 < $$A; done
+ cp upstream/darwin-linker/src/cctools/ld/fake-mach.c upstream/darwin-linker/src/cctools/libstuff
+ cd upstream/darwin-linker/src/; mkdir macosx-include; cd macosx-include; chmod +x ../../links.sh; ../../links.sh
+endif
+ make -C upstream/darwin-linker/src/cctools/libstuff
+ make -C upstream/darwin-linker/src/cctools/misc
+ touch upstream/darwin-linker/src/cctools/misc/makeUser.c upstream/darwin-linker/src/cctools/misc/make.h
+ make -C upstream/darwin-linker/src/cctools/misc libtool.NEW
+ make -C upstream/darwin-linker/src/cctools/ld; true
+ make -C upstream/darwin-linker/src/cctools/as
+ make -C upstream/darwin-linker/src/cctools/ar
+ mkdir -p upstream/install/powerpc-apple-darwin/bin
+ cp upstream/darwin-linker/src/cctools/ld/ld_dir/ld.NEW upstream/install/powerpc-apple-darwin/bin/ld.NEW
+ echo -e "#!/bin/sh\n"`pwd`"/upstream/install/powerpc-apple-darwin/bin/ld.NEW -dylib_file /usr/lib/system/libmathCommon.A.dylib:"`pwd`"/upstream/install/powerpc-apple-darwin/lib/libmathCommon.A.dylib -L"`pwd`"/upstream/install/powerpc-apple-darwin/lib \$$@\n" > upstream/install/powerpc-apple-darwin/bin/ld
+ chmod +x upstream/install/powerpc-apple-darwin/bin/ld
+ echo -e "#!/bin/sh\n"`pwd`"/upstream/install/powerpc-apple-darwin/bin/apple-libtool \$$1 -o \$$1\n" > upstream/install/powerpc-apple-darwin/bin/ranlib
+ chmod +x upstream/install/powerpc-apple-darwin/bin/ranlib
+ cp upstream/darwin-linker/src/cctools/misc/libtool.NEW upstream/install/powerpc-apple-darwin/bin/apple-libtool
+ cp upstream/darwin-linker/src/cctools/misc/strip.NEW upstream/install/powerpc-apple-darwin/bin/strip
+ cp upstream/darwin-linker/src/cctools/as/appc_dir/as upstream/install/powerpc-apple-darwin/bin/as
+ cp upstream/darwin-linker/src/cctools/ar/ar.NEW upstream/install/powerpc-apple-darwin/bin/ar.NEW
+ echo -e "#!/bin/sh\n"`pwd`"/upstream/install/powerpc-apple-darwin/bin/ar.NEW \$$@\n"`pwd`"/upstream/install/powerpc-apple-darwin/bin/ranlib \$$2\n" > upstream/install/powerpc-apple-darwin/bin/ar
+ chmod +x upstream/install/powerpc-apple-darwin/bin/ar
+ mkdir -p upstream/install/bin
+ cd upstream/install/bin; ln -sf ../powerpc-apple-darwin/bin/ld powerpc-apple-darwin-ld
+ cd upstream/install/bin; ln -sf ../powerpc-apple-darwin/bin/as powerpc-apple-darwin-as
+ cd upstream/install/bin; ln -sf ../powerpc-apple-darwin/bin/ar powerpc-apple-darwin-ar
+ cd upstream/install/bin; ln -sf ../powerpc-apple-darwin/bin/ranlib powerpc-apple-darwin-ranlib
+ cd upstream/install/bin; ln -sf ../powerpc-apple-darwin/bin/strip powerpc-apple-darwin-strip
+ echo -e "#!/bin/sh\nc++filt \$$@\n" > upstream/install/bin/c++filt3; chmod +x upstream/install/bin/c++filt3
+ cd upstream/install/powerpc-apple-darwin/bin; ln -sf ../../bin/c++filt3
+ touch $@
+
+
+# how to configure it
+ifneq ($(shell uname),$(platform))
+configure_gcc-3.3 += --with-headers=$(shell pwd)/upstream/install/$(target)/include
+endif
+configure_gcc-3.3 += --enable-languages=c,c++,java --enable-gc-type=boehm --disable-jvmpi --without-libffi
+configure_gcc-3.3 += --with-ld=$(shell pwd)/upstream/install/bin/$(target)-ld
+configure_gcc-3.3 += --with-as=$(shell pwd)/upstream/install/bin/$(target)-as
+ifneq ($(platform),Darwin)
+configure_gcc-3.3 += --with-gnu-ld --with-gnu-as
+endif
+configure_gcc-3.3 += --disable-java-awt --disable-interpreter --enable-libgcj
+configure_gcc-3.3 += --disable-shared --enable-static --disable-jni
+configure_binutils-2.13.2.1 += --disable-shared --enable-static
+configure_gcc-3.3_powerpc-apple-darwin += --enable-threads=posix --disable-hash-synchronization --disable-multilib
+configure_gcc-3.3_i686-pc-mingw32 += --enable-threads=win32 --enable-hash-synchronization
+configure_gcc-3.3_i686-pc-linux-gnu += --enable-threads=posix --enable-hash-synchronization
+configure_gcc-3.3_sparc-sun-solaris2.7 += --enable-threads=posix --disable-hash-synchronization --disable-multilib
+
+configure_WindowMaker-0.80.2_$(target) += --prefix=$(shell pwd)/upstream/install/$(target)
+configure_WindowMaker-0.80.2_$(target) += --host=i686-pc-linux-gnu --x-libraries=$(shell pwd)/upstream/install/$(target)/lib
+
+#environment_gcc-3.3_i686-pc-linux-gnu += CFLAGS="-Wl,-ldl"
+
+# libjpeg's configury doesn't obey --target
+environment_jpeg-6b_$(target) += PATH=$(shell pwd)/upstream/install/$(target)/bin:$$PATH
+environment_jpeg-6b_$(target) += CC=$(shell pwd)/upstream/install/bin/$(target)-gcc
+environment_jpeg-6b_$(target) += CFLAGS="-Os -ffunction-sections -fdata-sections -I ."
+environment_jpeg-6b_$(target) += AR="$(shell pwd)/upstream/install/$(target)/bin/ar rc"
+environment_jpeg-6b_$(target) += AR2=$(shell pwd)/upstream/install/$(target)/bin/ranlib
+
+# libmspack configury doesn't obey --target
+environment_libmspack-20030726_$(target) += PATH=$(shell pwd)/upstream/install/$(target)/bin:$$PATH
+environment_libmspack-20030726_$(target) += CC=$(shell pwd)/upstream/install/bin/$(target)-gcc
+environment_libmspack-20030726_$(target) += AR="$(shell pwd)/upstream/install/$(target)/bin/ar"
+environment_libmspack-20030726_$(target) += AR2=$(shell pwd)/upstream/install/$(target)/bin/ranlib
+environment_libmspack-20030726_$(target) += OPTIM="-ffunction-sections -fdata-sections -O3"
+
+# WindowMaker configury doesn't obey --target
+environment_WindowMaker-0.80.2_$(target) += PATH=$(shell pwd)/upstream/install/$(target)/bin:$$PATH
+environment_WindowMaker-0.80.2_$(target) += CC=$(shell pwd)/upstream/install/bin/$(target)-gcc
+environment_WindowMaker-0.80.2_$(target) += AR="$(shell pwd)/upstream/install/$(target)/bin/ar"
+environment_WindowMaker-0.80.2_$(target) += AR2=$(shell pwd)/upstream/install/$(target)/bin/ranlib
+environment_WindowMaker-0.80.2_$(target) += LDFLAGS="-lXext -ldl"
+make_install_WindowMaker-0.80.2_$(target) := -C WINGs install; make -C wrlib
+make_WindowMaker-0.80.2_$(target) := WINGs
+
+environment_gcc_3.3_$(target) += PATH=$(shell pwd)/upstream/install/bin:$$PATH
+
+.PRECIOUS: .vendor .download_% .configure_%_$(target) .install_%_$(target)
+
+.download_org.ibex.%:
+ @echo -e "\033[1mfetching repository org.ibex.$*\033[0m"
+ @mkdir -p upstream; cd upstream; rm -rf org.ibex.$*; rm -rf org.ibex.$*_*
+ @cd upstream; darcs get --verbose --partial --repo-name=org.ibex.$* http://$*.ibex.org
+ @touch $@
+
+.build_org.ibex.nestedvm: .vendor .download_org.ibex.nestedvm
+ cd upstream/org.ibex.nestedvm; make usr=$(shell pwd)/upstream/install all env.sh
+ touch $@
+
+.install_org.ibex.nestedvm: .build_org.ibex.nestedvm
+ touch $@
+
+# vendor-supplied binaries and headers; this is stuff that comes with various OSes
+vendor: .vendor; @true
+.vendor:
+ @echo -e "\n\033[1mdownloading vendor-supplied headers and libraries...\033[0m"
+ mkdir -p upstream/install
+ curl http://www.megacz.com/vendor.tgz | tar xzf - -C upstream/install
+ mkdir -p upstream/install/i686-pc-mingw32
+ curl $(url_w32api-2.3) | tar xzf - -C upstream/install/i686-pc-mingw32
+ curl $(url_mingw-runtime-3.0) | tar xzf - -C upstream/install/i686-pc-mingw32
+ifeq ($(shell uname),Linux)
+ cd upstream/install/i686-pc-linux-gnu/include; rm -rf *; ln -sf /usr/include/* .
+endif
+ touch .vendor
+
+.download_gcc-3.3_powerpc-apple-darwin:
+ @echo -e "\n\033[1mdownloading $*...\033[0m"
+ mkdir -p upstream/gcc-3.3
+ curl $(url_gcc-3.3) | tar xzf - -C upstream/gcc-3.3
+ mv upstream/gcc-3.3/gcc-3.3 upstream/gcc-3.3/src-darwin; true
+ mv upstream/gcc-3.3/libmspack upstream/gcc-3.3/src-darwin; true
+ (cd upstream/gcc-3.3/src-darwin && for A in ../patches/[a-y]*.patch; do patch -p0 -l < $$A; done); true
+ (cd upstream/gcc-3.3/src-darwin && for A in ../patches-darwin/*.patch; do patch -p0 -l < $$A; done); true
+ touch $@
+
+.download_%:
+ @echo -e "\n\033[1mdownloading $*...\033[0m"
+ mkdir -p upstream/$*
+ curl $(url_$*) | tar xzf - -C upstream/$*
+ mv upstream/$*/$* upstream/$*/src; true
+ mv upstream/$*/libmspack upstream/$*/src; true
+ (test -e upstream/$*/patches && cd upstream/$*/src && for A in ../patches/*.patch; do patch -p0 -l < $$A; done); true
+ touch $@
+
+.configure_gcc-3.3_powerpc-apple-darwin: .download_gcc-3.3_powerpc-apple-darwin .install_binutils-2.13.2.1_powerpc-apple-darwin
+ @echo -e "\n\033[1mconfiguring gcc...\033[0m"
+ mkdir -p upstream/gcc-3.3/build-$(target)
+ cd upstream/gcc-3.3/build-$(target); \
+ $(setcc) $(environment_gcc-3.3_$(target)) ../src-darwin/configure \
+ --target=$(target) \
+ --prefix=`cd ../..; pwd`/install \
+ $(configure_gcc-3.3) \
+ $(configure_gcc-3.3_$(target))
+ touch $@
+
+.configure_gcc-3.3_$(target): .install_binutils-2.13.2.1_$(target)
+
+.configure_%_$(target): .vendor .download_%
+ @echo -e "\n\033[1mconfiguring $*...\033[0m"
+ mkdir -p upstream/$*/build-$(target)
+ cd upstream/$*/build-$(target); \
+ $(setcc) $(environment_$*_$(target)) ../src/configure \
+ --target=$(target) \
+ --prefix=`cd ../..; pwd`/install \
+ $(configure_$*) \
+ $(configure_$*_$(target))
+ touch $@
+
+.configure_libmspack-20030726_$(target): .download_libmspack-20030726 .install_gcc-3.3_$(target)
+ mkdir -p upstream/libmspack-20030726/build-$(target)
+ cd upstream/libmspack-20030726/build-$(target); ln -sf ../src/mspack/* .
+ touch $@
+
+.install_freetype-2.1.4_mips-unknown-elf: .install_org.ibex.nestedvm .download_freetype-2.1.4
+ cd upstream/freetype-2.1.4/src; \
+ make setup ansi; \
+ PATH=$$PATH:`pwd`/../../install/bin make \
+ CC=mips-unknown-elf-gcc \
+ AR=mips-unknown-elf-ar \
+ CFLAGS="-c -ffunction-sections -fdata-sections -O3"
+ upstream/install/bin/mips-unknown-elf-ranlib upstream/freetype-2.1.4/src/objs/libfreetype.a
+ touch $@
+
+.build_%_$(target): .configure_%_$(target)
+ @echo -e "\n\033[1mbuilding $*...\033[0m"
+ cd upstream/$*/build-$(target); \
+ $(setcc) PATH=$$PATH:`pwd`/../../install/bin \
+ $(environment_$*_$(target)) \
+ make $(make_$*_$(target)) $(setcc) $(environment_$*_$(target))
+ touch $@
+
+.install_libmspack-20030726_$(target): .build_libmspack-20030726_$(target); true
+
+.install_gcc-3.3_mips-unknown-elf: .install_org.ibex.nestedvm
+ touch $@
+
+.install_%_$(target): .build_%_$(target)
+ @echo -e "\n\033[1minstalling $*...\033[0m"
+ cd upstream/$*/build-$(target); \
+ $(setcc) PATH=$$PATH:`pwd`/../../install/bin \
+ $(environment_$*_$(target)) \
+ make $(make_install_$*_$(target)) install $(setcc) $(environment_$*_$(target))
+ touch $@
+
+
+
diff --git a/upstream/org.ibex.core/README b/upstream/org.ibex.core/README
new file mode 100644
index 0000000..095be99
--- /dev/null
+++ b/upstream/org.ibex.core/README
@@ -0,0 +1,104 @@
+==============================================================================
+Ibex README
+
+Test
+
+______________________________________________________________________________
+Documentation
+
+If you're new to Ibex, you should read the documentation in the order
+presented below:
+
+- README [this file]
+
+ Start here. Includes a map of all other documentation and a
+ description of the directory strucure
+
+- The Ibex home page [http://www.ibex.org/]
+
+- The Ibex tutorials [http://www.ibex.org/tutorials.html]
+
+ Gentle introduction to what Ibex is, and how to write
+ .ibex's. Good for anybody who wants to write Ibex applications.
+
+- The Ibex reference [http://www.ibex.org/reference.html]
+
+ Precise, technical spec of exactly how Ibex works. Assumes
+ familiarity with Ibex (ie, you've read the tutorial). This is the
+ authoritative source for how an Ibex engine should
+ behave.
+
+
+______________________________________________________________________________
+Directory Structure
+
+/
+ AUTHORS - people involved in developing Ibex
+ README - this file
+ COPYING - copyright information for all files in this distro
+ CHANGES - the changelot
+ TM - trademark information for Ibex
+ Makefile - build file for gcc 3.3
+ next.build - the build id of the next build to be generated
+
+ build/ - anything generated by the build process winds up here
+ h/ - header files generated by gcjh
+ java/ - auto-generated .java source files
+ class/ - java .class files
+ xwar/ - generated xwars (mainly builtin.xwar)
+ mips/ - mips binaries (interpreted on all platforms)
+ JVM/ - java Jar archives
+ Linux/ - Linux binaries
+ Win32/ - Win32 binaries
+ Darwin/ - Darwin binaries
+ Solaris/ - Solaris binaries
+
+ Makefile.upstream - build/download/install instructions for upstream packages
+ upstream/ - download, build, and install area for upstream packages
+ install/ - this is the --prefix for all configure invocations
+ gcc-3.3/ - build/download area for gcc
+ jpeg-6b/ - build/download area for libjpeg
+ freetype-2.1.4/ - build/download area for freetype
+ newlib-1.11.0/ - build/download area for newlib
+ darwin-linker/ - build/download area for a hacked version of Apple's binutils
+
+ lib/ - any third-party binary stuff needed during the build process
+ javago - a copy of the javago post-compilation bytecode inliner
+ jump.jar - the jump2 bytecode-to-palmos translator
+ libgcj-minimal.jar - a tiny portion of libgcj.jar; allows java->bytecode compilation without gcj
+
+ src/ - all java source files and ibex sources go here
+ edu/stanford/ejalbr - source code for BrowserLauncher
+ gnu/regexp/ - source code for the GNU regexp library for Java
+ org/
+ bouncycastle/ - the BouncyCastle Crypto Library
+ ibex/
+ translators/ - .ibex's and .png's that are essential to bootstrapping the engine
+ plat/ - platform-specific code
+ util/ - misc utilities
+ js/ - the Ibex JavaScript Interpreter
+ mips/ - the Ibex MIPS interpreter/recompiler
+ builtin/ - content for the core builtin resource
+
+
+______________________________________________________________________________
+Build Requirements
+
+There are pre-built, digitally signed binaries on http://dist.ibex.org/
+for every supported platform. Please consider using those unless
+you're absolutely certain you need to go through the trouble of trying
+to get Ibex to build.
+
+Requirements:
+ - jdk 1.3+
+ - the standard set of POSIX utilities (wc, grep, find, make, etc)
+
+Build Targets:
+ - compile: compiles all .java files into build/class/
+ - JVM: Any true Java JVM (ie not GCJ)
+ - Win32: Win95 OSR2 or later
+ - Linux: Linux 2.2 or later
+ - Darwin: Any Darwin-based OS (Mac OS X, OpenDarwin)
+ - Solaris: Solaris 2.7+
+ - clean: always run this after doing a 'cvs update'
+
diff --git a/upstream/org.ibex.core/build/java/org/ibex/core/Box.java b/upstream/org.ibex.core/build/java/org/ibex/core/Box.java
new file mode 100644
index 0000000..2d12964
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/core/Box.java
@@ -0,0 +1,943 @@
+// FIXME
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.core;
+
+// FIXME: are traps on x/y meaningful?
+// FIXME: if we trap on cols, then set rows to 0 (forcing cols to 1), does the cols trap get triggered?
+// FIXME: if we change min{width/height}, thereby forcing a change to max{min/height}, does a trap on those get triggered?
+// FIXME: trap on numchildren? replaces ChildChanged?
+// FIXME: trap on visible, trigger when parent visibility changes
+
+// FIXME: ax/ay nonsense
+// FIXME: mouse move/release still needs to propagate to boxen in which the mouse was pressed and is still held down
+
+// FEATURE: mark to reflow starting with a certain child
+// FEATURE: reintroduce surface.abort
+
+import java.util.*;
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.graphics.*;
+
+/**
+ *
+ * Encapsulates the data for a single Ibex box as well as all layout
+ * rendering logic.
+ *
+ *
+ *
The rendering process consists of four phases; each requires
+ * one DFS pass over the tree
+ *
pack(): each box sets its childrens' row/col
+ *
constrain(): contentwidth is computed
+ *
resize(): width/height and x/y positions are set
+ *
render(): children draw their content onto the PixelBuffer.
+ *
+ * Implementations of the Platform class should return objects
+ * supporting this interface from the _createPixelBuffer()
+ * method. These implementations may choose to use off-screen video
+ * ram for this purpose (for example, a Pixmap on X11).
+ *
+ *
+ *
+ * Many of these functions come in pairs, one that uses ints and one
+ * that uses floats. The int functions are intended for situations
+ * in which the CTM is the identity transform.
+ *
+ */
+public abstract class PixelBuffer {
+
+ /** draw the picture at (dx1, dy1), cropping to (cx1, cy1, cx2, cy2) */
+ public abstract void drawPicture(Picture source, int dx1, int dy1, int cx1, int cy1, int cx2, int cy2);
+
+ /** fill a trapezoid whose top and bottom edges are horizontal */
+ public abstract void fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, int color);
+
+ /**
+ * Same as drawPicture, but only uses the alpha channel of the Picture, and is allowed to destructively modify the RGB
+ * channels of the Picture in the process. This method may assume that the RGB channels of the image are all zero IFF it
+ * restores this invariant before returning.
+ */
+ public abstract void drawGlyph(Font.Glyph source, int dx1, int dy1, int cx1, int cy1, int cx2, int cy2, int rgb);
+
+ // FEATURE: we want floats (inter-pixel spacing) for antialiasing, but this hoses the fastpath line drawing... argh!
+ /** draws a line of width w; note that the coordinates here are post-transform */
+ public void drawLine(int x1, int y1, int x2, int y2, int w, int color, boolean capped) {
+
+ if (y1 > y2) { int t = x1; x1 = x2; x2 = t; t = y1; y1 = y2; y2 = t; }
+
+ if (x1 == x2) {
+ fillTrapezoid(x1 - w / 2, x2 + w / 2, y1 - (capped ? w / 2 : 0),
+ x1 - w / 2, x2 + w / 2, y2 + (capped ? w / 2 : 0), color);
+ return;
+ }
+
+ // fastpath for single-pixel width lines
+ if (w == 1) {
+ float slope = (float)(y2 - y1) / (float)(x2 - x1);
+ int last_x = x1;
+ for(int y=y1; y<=y2; y++) {
+ int new_x = (int)((float)(y - y1) / slope) + x1;
+ if (slope >= 0) fillTrapezoid(last_x + 1, y != y2 ? new_x + 1 : new_x, y,
+ last_x + 1, y != y2 ? new_x + 1 : new_x, y + 1, color);
+ else fillTrapezoid(y != y2 ? new_x : new_x + 1, last_x, y,
+ y != y2 ? new_x : new_x + 1, last_x, y + 1, color);
+ last_x = new_x;
+ }
+ return;
+ }
+
+ // actually half-width
+ float width = (float)w / 2;
+ float phi = (float)Math.atan((y2 - y1) / (x2 - x1));
+ if (phi < 0.0) phi += (float)Math.PI * 2;
+ float theta = (float)Math.PI / 2 - phi;
+
+ // dx and dy are the x and y distance between each endpoint and the corner of the stroke
+ int dx = (int)(width * Math.cos(theta));
+ int dy = (int)(width * Math.sin(theta));
+
+ // slice is the longest possible length of a horizontal line across the stroke
+ int slice = (int)(2 * width / Math.cos(theta));
+
+ if (capped) {
+ x1 -= width * Math.cos(phi);
+ x2 += width * Math.cos(phi);
+ y1 -= width * Math.sin(phi);
+ y2 += width * Math.sin(phi);
+ }
+
+ fillTrapezoid(x1 + dx, x1 + dx, y1 - dy, x1 - dx, x1 - dx + slice, y1 + dy, color); // top corner
+ fillTrapezoid(x2 + dx - slice, x2 + dx, y2 - dy, x2 - dx, x2 - dx, y2 + dy, color); // bottom corner
+ fillTrapezoid(x1 - dx, x1 - dx + slice, y1 + dy, x2 + dx - slice, x2 + dx, y2 - dy, color); // middle
+ }
+
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/graphics/SVG.java b/upstream/org.ibex.core/build/java/org/ibex/graphics/SVG.java
new file mode 100644
index 0000000..90311a6
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/graphics/SVG.java
@@ -0,0 +1,301 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.graphics;
+import java.util.*;
+
+
+// FIXME: offer a "subpixel" mode where we pass floats to the Platform and don't do any snapping
+// FIXME: fracture when realizing instead of when parsing?
+
+/*
+ v1.0
+ - textpath
+ - gradients
+ - patterns
+ - clipping/masking
+ - filters (filtering of a group must be performed AFTER the group is assembled; sep. canvas)
+
+ v1.1
+ - bump caps [requires Paint that can fill circles...] [remember to distinguish between closed/unclosed]
+ - line joins
+ - mitre (hard)
+ - bevel (easy)
+ - bump (easy, but requires 'round' Paint)
+ - subtree sharing? otherwise the memory consumption might be outrageous... clone="" attribute?
+ - better clipping
+ - intersect clip regions (linearity)
+ - clip on trapezoids, not pixels
+ - faster gradients and patterns:
+ - transform each corner of the trapezoid and then interpolate
+*/
+
+// FIXME: need to support style sheets and the 'style=' attribute
+// FIXME: need to convert markers into subboxes
+public class SVG {
+
+ /*
+ public static void parseNode(String name, String[] keys, Object[] vals, Template t) {
+ Hash h = new Hash();
+ for(int i=0; iname property * /
+ Hashtable glyphByName = new Hashtable();
+
+ / ** linked list of glyphs, stored by the first character of their unicode property * /
+ Hashtable glyphByUnicode = new Hashtable();
+
+ // a Glyph in an VectorGraphics font
+ public static class Glyph {
+
+ // FIXME: lang attribute
+ boolean isVerticallyOriented = false;
+ Template t = null;
+ Box b = null;
+
+ float horiz_adv_x = 0;
+ float vert_origin_x = 0;
+ float vert_origin_y = 0;
+ float vert_adv_y = 0;
+
+ String unicode = null;
+
+ // forms the linked list in glyphByUnicode; glyphs appear in the order specified in the font
+ public Glyph next = null;
+
+ Glyph(String name, String unicode, Template t, VectorGraphics.Font f) {
+ if (unicode != null)
+ if (f.glyphByUnicode.get(unicode.substring(0, 1)) == null) {
+ f.glyphByUnicode.put(unicode.substring(0, 1), this);
+ } else {
+ Glyph g;
+ for(g = (Glyph)f.glyphByUnicode.get(unicode.substring(0, 1)); g.next != null; g = g.next);
+ g.next = this;
+ }
+ if (name != null) f.glyphByUnicode.put(name, this);
+ this.unicode = unicode;
+ this.t = t;
+ horiz_adv_x = f.horiz_adv_x;
+ vert_origin_x = f.vert_origin_x;
+ vert_origin_y = f.vert_origin_y;
+ vert_adv_y = f.vert_adv_y;
+ }
+ public void render(DoubleBuffer buf, int x, int y, int fillcolor, int strokecolor, float scaleFactor) {
+ // FEATURE: make b double-buffered for increased performance
+ if (b == null) {
+ b = new Box(t, new org.ibex.util.Vec(), new org.ibex.util.Vec(), null, 0, 0);
+ b.put("absolute", Boolean.TRUE);
+ b.prerender();
+ t = null;
+ }
+ // FIXME
+ b.put("width", new Integer(1000));
+ b.put("height", new Integer(1000));
+ b.fillcolor = fillcolor;
+ b.strokecolor = strokecolor;
+
+ // we toss an extra flip on the ctm so that fonts stick "up" instead of down
+ b.render(0, 0, buf.getWidth(), buf.getHeight(), buf,
+ Affine.flip(false, true).multiply(Affine.scale(scaleFactor, scaleFactor).multiply(Affine.translate(x, y))).multiply(buf.a));
+ }
+ }
+ }
+ */
+
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/graphics/Surface.java b/upstream/org.ibex.core/build/java/org/ibex/graphics/Surface.java
new file mode 100644
index 0000000..31ac36e
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/graphics/Surface.java
@@ -0,0 +1,405 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.graphics;
+
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.plat.*;
+
+import org.ibex.core.*; // FIXME
+
+/**
+ * A Surface, as described in the Ibex Reference.
+ *
+ * Platform subclasses should include an inner class subclass of
+ * Surface to return from the Platform._createSurface() method
+ */
+public abstract class Surface extends PixelBuffer implements Task {
+
+ // Static Data ////////////////////////////////////////////////////////////////////////////////
+
+ private static Boolean T = Boolean.TRUE;
+ private static Boolean F = Boolean.FALSE;
+
+ /** all instances of Surface which need to be refreshed by the Scheduler */
+ public static Vec allSurfaces = new Vec();
+
+ /** When set to true, render() should abort as soon as possible and restart the rendering process */
+ public volatile boolean abort = false;
+
+ // these three variables are used to ensure that user resizes trump programmatic resizes
+ public volatile boolean syncRootBoxToSurface = false;
+ public volatile int pendingWidth = 0;
+ public volatile int pendingHeight = 0;
+
+ public static boolean alt = false; ///< true iff the alt button is pressed down
+ public static boolean control = false; ///< true iff the control button is pressed down
+ public static boolean shift = false; ///< true iff the shift button is pressed down
+ public static boolean button1 = false; ///< true iff button 1 is depressed
+ public static boolean button2 = false; ///< true iff button 2 is depressed
+ public static boolean button3 = false; ///< true iff button 3 is depressed
+
+
+ // Instance Data ///////////////////////////////////////////////////////////////////////
+
+ public Box root; ///< The Box at the root of this surface
+ public String cursor = "default"; ///< The active cursor to switch to when syncCursor() is called
+ public int mousex; ///< x position of the mouse
+ public int mousey; ///< y position of the mouse
+ public int _mousex; ///< x position of the mouse FIXME
+ public int _mousey; ///< y position of the mouse FIXME
+ public int newmousex = -1; ///< x position of the mouse, in real time; this lets us collapse Move's
+ public int newmousey = -1; ///< y position of the mouse, in real time; this lets us collapse Move's
+ public boolean minimized = false; ///< True iff this surface is minimized, in real time
+ public boolean maximized = false; ///< True iff this surface is maximized, in real time
+ public boolean unrendered = true; ///< True iff this surface has not yet been rendered
+ DirtyList dirtyRegions = new DirtyList(); ///< Dirty regions on the surface
+
+ // Used For Simulating Clicks and DoubleClicks /////////////////////////////////////////////////
+
+ int last_press_x = Integer.MAX_VALUE; ///< the x-position of the mouse the last time a Press message was enqueued
+ int last_press_y = Integer.MAX_VALUE; ///< the y-position of the mouse the last time a Press message was enqueued
+ static int lastClickButton = 0; ///< the last button to recieve a Click message; used for simulating DoubleClick's
+ static long lastClickTime = 0; ///< the last time a Click message was processed; used for simulating DoubleClick's
+
+
+ // Methods to be overridden by subclasses ///////////////////////////////////////////////////////
+
+ public abstract void toBack(); ///< should push surface to the back of the stacking order
+ public abstract void toFront(); ///< should pull surface to the front of the stacking order
+ public abstract void syncCursor(); ///< set the actual cursor to this.cursor if they do not match
+ public abstract void setInvisible(boolean b); ///< If b, make window invisible; otherwise, make it non-invisible.
+ protected abstract void _setMaximized(boolean b); ///< If b, maximize the surface; otherwise, un-maximize it.
+ protected abstract void _setMinimized(boolean b); ///< If b, minimize the surface; otherwise, un-minimize it.
+ public abstract void setLocation(); ///< Set the surface's x/y position to that of the root box
+ protected abstract void _setSize(int w, int h); ///< set the actual size of the surface
+ public abstract void setTitleBarText(String s); ///< Sets the surface's title bar text, if applicable
+ public abstract void setIcon(Picture i); ///< Sets the surface's title bar text, if applicable
+ public abstract void _dispose(); ///< Destroy the surface
+ public void setMinimumSize(int minx, int miny, boolean resizable) { }
+ protected void setSize(int w, int h) { _setSize(w, h); }
+
+ public static Picture scarImage = null;
+
+ // Helper methods for subclasses ////////////////////////////////////////////////////////////
+
+ protected final void Press(final int button) {
+ last_press_x = mousex;
+ last_press_y = mousey;
+
+ if (button == 1) button1 = true;
+ else if (button == 2) button2 = true;
+ else if (button == 3) button3 = true;
+
+ if (button == 1) new Message("_Press1", T, root);
+ else if (button == 2) new Message("_Press2", T, root);
+ else if (button == 3) {
+ Scheduler.add(new Task() { public void perform() throws JSExn {
+ Platform.clipboardReadEnabled = true;
+ try {
+ root.putAndTriggerTraps("_Press3", T);
+ } finally {
+ Platform.clipboardReadEnabled = false;
+ }
+ }});
+ }
+ }
+
+ protected final void Release(int button) {
+ if (button == 1) button1 = false;
+ else if (button == 2) button2 = false;
+ else if (button == 3) button3 = false;
+
+ if (button == 1) new Message("_Release1", T, root);
+ else if (button == 2) new Message("_Release2", T, root);
+ else if (button == 3) new Message("_Release3", T, root);
+
+ if (Platform.needsAutoClick() && Math.abs(last_press_x - mousex) < 5 && Math.abs(last_press_y - mousey) < 5) Click(button);
+ last_press_x = Integer.MAX_VALUE;
+ last_press_y = Integer.MAX_VALUE;
+ }
+
+ protected final void Click(int button) {
+ if (button == 1) new Message("_Click1", T, root);
+ else if (button == 2) new Message("_Click2", T, root);
+ else if (button == 3) new Message("_Click3", T, root);
+ if (Platform.needsAutoDoubleClick()) {
+ long now = System.currentTimeMillis();
+ if (lastClickButton == button && now - lastClickTime < 350) DoubleClick(button);
+ lastClickButton = button;
+ lastClickTime = now;
+ }
+ }
+
+ /** we enqueue ourselves in the Scheduler when we have a Move message to deal with */
+ private Task mover = new Task() {
+ public void perform() {
+ if (mousex == newmousex && mousey == newmousey) return;
+ int oldmousex = mousex; mousex = newmousex;
+ int oldmousey = mousey; mousey = newmousey;
+ String oldcursor = cursor; cursor = "default";
+ // FIXME: Root (ONLY) gets motion events outside itself (if trapped)
+ if (oldmousex != mousex || oldmousey != mousey)
+ root.putAndTriggerTrapsAndCatchExceptions("_Move", T);
+ if (!cursor.equals(oldcursor)) syncCursor();
+ } };
+
+ /**
+ * Notify Ibex that the mouse has moved. If the mouse leaves the
+ * surface, but the host windowing system does not provide its new
+ * position (for example, a Java MouseListener.mouseExited()
+ * message), the subclass should use (-1,-1).
+ */
+ protected final void Move(final int newmousex, final int newmousey) {
+ this.newmousex = newmousex;
+ this.newmousey = newmousey;
+ Scheduler.add(mover);
+ }
+
+ protected final void HScroll(int pixels) { new Message("_HScroll", new Integer(pixels), root); }
+ protected final void VScroll(int pixels) { new Message("_VScroll", new Integer(pixels), root); }
+ protected final void HScroll(float lines) { new Message("_HScroll", new Float(lines), root); }
+ protected final void VScroll(float lines) { new Message("_VScroll", new Float(lines), root); }
+
+ /** subclasses should invoke this method when the user resizes the window */
+ protected final void SizeChange(final int width, final int height) {
+ if (unrendered || (pendingWidth == width && pendingHeight == height)) return;
+ pendingWidth = width;
+ pendingHeight = height;
+ syncRootBoxToSurface = true;
+ abort = true;
+ Scheduler.renderAll();
+ }
+
+ // FEATURE: can we avoid creating objects here?
+ protected final void PosChange(final int x, final int y) {
+ Scheduler.add(new Task() { public void perform() throws JSExn {
+ root.x = x;
+ root.y = y;
+ root.putAndTriggerTrapsAndCatchExceptions("PosChange", T);
+ }});
+ }
+
+ private final String[] doubleClick = new String[] { null, "_DoubleClick1", "_DoubleClick2", "_DoubleClick3" };
+ protected final void DoubleClick(int button) { new Message(doubleClick[button], T, root); }
+ protected final void KeyPressed(String key) { new Message("_KeyPressed", key, root); }
+ protected final void KeyReleased(String key) { new Message("_KeyReleased", key, root); }
+ protected final void Close() { new Message("Close", T, root); }
+ protected final void Minimized(boolean b) { minimized = b; new Message("Minimized", b ? T : F, root); }
+ protected final void Maximized(boolean b) { maximized = b; new Message("Maximized", b ? T : F, root); }
+ protected final void Focused(boolean b) { new Message("Focused", b ? T : F, root); }
+
+ private boolean scheduled = false;
+ public void Refresh() { if (!scheduled) Scheduler.add(this); scheduled = true; }
+ public void perform() { scheduled = false; Scheduler.renderAll(); }
+
+ public final void setMaximized(boolean b) { if (b != maximized) _setMaximized(maximized = b); }
+ public final void setMinimized(boolean b) { if (b != minimized) _setMinimized(minimized = b); }
+
+
+ // Other Methods ///////////////////////////////////////////////////////////////////////////////
+
+ /** Indicates that the Surface is no longer needed */
+ public final void dispose(boolean quitIfAllSurfacesGone) {
+ if (Log.on) Log.info(this, "disposing " + this);
+ allSurfaces.removeElement(this);
+ _dispose();
+ if (allSurfaces.size() == 0) {
+ if (Log.on) Log.info(this, "exiting because last surface was destroyed");
+ System.exit(0);
+ }
+ }
+
+ public void dirty(int x, int y, int w, int h) {
+ dirtyRegions.dirty(x, y, w, h);
+ Refresh();
+ }
+
+ public static Surface fromBox(Box b) {
+ // FIXME use a hash table here
+ for(int i=0; i root.width) w = root.width - x;
+ if (y+h > root.height) h = root.height - y;
+ if (w <= 0 || h <= 0) continue;
+
+ root.render(0, 0, x, y, x + w, y + h, this, identity);
+ drawPicture(scarImage, 0, root.height - scarImage.height, x, y, x+w, y+h);
+
+ if (abort) {
+ // x,y,w,h is only partially reconstructed, so we must be careful not to re-blit it
+ dirtyRegions.dirty(x, y, w, h);
+ // put back all the dirty regions we haven't yet processed (including the current one)
+ for(int j=i; j root.width) w = root.width - x;
+ if (y+h > root.height) h = root.height - y;
+ if (w <= 0 || h <= 0) continue;
+ if (abort) return;
+ blit(backbuffer, x, y, x, y, w + x, h + y);
+ }
+ }
+
+ /** This is how subclasses signal a 'shallow dirty', indicating that although the backbuffer is valid, the screen is not */
+ public final void Dirty(int x, int y, int w, int h) {
+ screenDirtyRegions.dirty(x, y, w, h);
+ Scheduler.renderAll();
+ }
+
+ public void dirty(int x, int y, int w, int h) {
+ screenDirtyRegions.dirty(x, y, w, h);
+ super.dirty(x, y, w, h);
+ }
+
+ /** copies a region from the doublebuffer to this surface */
+ public abstract void blit(PixelBuffer source, int sx, int sy, int dx, int dy, int dx2, int dy2);
+
+ }
+
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/js/ByteCodes.java b/upstream/org.ibex.core/build/java/org/ibex/js/ByteCodes.java
new file mode 100644
index 0000000..e8170f1
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/js/ByteCodes.java
@@ -0,0 +1,94 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+/**
+ * Constants for the various JavaScript ByteCode operations.
+ *
+ * Each instruction is an opcode and an optional literal literal;
+ * some Tokens are also valid; see Tokens.java
+ */
+interface ByteCodes {
+
+ /** push the literal onto the stack */
+ public static final byte LITERAL = -2;
+
+ /** push a new array onto the stack with length equal to the literal */
+ public static final byte ARRAY = -3;
+
+ /** push an empty object onto the stack */
+ public static final byte OBJECT = -4;
+
+ /** create a new instance; literal is a reference to the corresponding ForthBlock */
+ public static final byte NEWFUNCTION = -5;
+
+ /** if given a non-null argument declare its argument in the current scope and push
+ it to the stack, else, declares the element on the top of the stack and leaves it
+ there */
+ public static final byte DECLARE = -6;
+
+ /** push a reference to the current scope onto the stack */
+ public static final byte TOPSCOPE = -7;
+
+ /** if given a null literal pop two elements off the stack; push stack[-1].get(stack[top])
+ else pop one element off the stack, push stack[top].get(literal) */
+ public static final byte GET = -8;
+
+ /** push stack[-1].get(stack[top]) */
+ public static final byte GET_PRESERVE = -9;
+
+ /** pop two elements off the stack; stack[-2].put(stack[-1], stack[top]); push stack[top] */
+ public static final byte PUT = -10;
+
+ /** literal is a relative address; pop stacktop and jump if the value is true */
+ public static final byte JT = -11;
+
+ /** literal is a relative address; pop stacktop and jump if the value is false */
+ public static final byte JF = -12;
+
+ /** literal is a relative address; jump to it */
+ public static final byte JMP = -13;
+
+ /** discard the top stack element */
+ static public final byte POP = -14;
+
+ /** pop element; call stack[top](stack[-n], stack[-n+1]...) where n is the number of args to the function */
+ public static final byte CALL = -15;
+
+ /** pop an element; push a JS.JSArray containing the keys of the popped element */
+ public static final byte PUSHKEYS = -16;
+
+ /** push the top element down so that (arg) elements are on top of it; all other elements retain ordering */
+ public static final byte SWAP = -17;
+
+ /** execute the bytecode block pointed to by the literal in a fresh scope with parentScope==THIS */
+ public static final byte NEWSCOPE = -18;
+
+ /** execute the bytecode block pointed to by the literal in a fresh scope with parentScope==THIS */
+ public static final byte OLDSCOPE = -19;
+
+ /** push a copy of the top stack element */
+ public static final byte DUP = -20;
+
+ /** a NOP; confers a label upon the following instruction */
+ public static final byte LABEL = -21;
+
+ /** execute the ForthBlock pointed to by the literal until BREAK encountered; push TRUE onto the stack for the first iteration
+ * and FALSE for all subsequent iterations */
+ public static final byte LOOP = -22;
+
+ /** similar effect a a GET followed by a CALL */
+ public static final byte CALLMETHOD = -23;
+
+ /** finish a finally block and carry out whatever instruction initiated the finally block */
+ public static final byte FINALLY_DONE = -24;
+
+ /** finish a finally block and carry out whatever instruction initiated the finally block */
+ public static final byte MAKE_GRAMMAR = -25;
+
+ public static final String[] bytecodeToString = new String[] {
+ "", "", "LITERAL", "ARRAY", "OBJECT", "NEWFUNCTION", "DECLARE", "TOPSCOPE",
+ "GET", "GET_PRESERVE", "PUT", "JT", "JF", "JMP", "POP", "CALL", "PUSHKEYS",
+ "SWAP", "NEWSCOPE", "OLDSCOPE", "DUP", "LABEL", "LOOP", "CALLMETHOD",
+ "FINALLY_DONE", "MAKE_GRAMMAR"
+ };
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/js/Interpreter.java b/upstream/org.ibex.core/build/java/org/ibex/js/Interpreter.java
new file mode 100644
index 0000000..0a4e52e
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/js/Interpreter.java
@@ -0,0 +1,744 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+import org.ibex.util.*;
+import java.util.*;
+
+/** Encapsulates a single JS interpreter (ie call stack) */
+class Interpreter implements ByteCodes, Tokens {
+
+
+ // Thread-Interpreter Mapping /////////////////////////////////////////////////////////////////////////
+
+ static Interpreter current() { return (Interpreter)threadToInterpreter.get(Thread.currentThread()); }
+ private static Hashtable threadToInterpreter = new Hashtable();
+
+
+ // Instance members and methods //////////////////////////////////////////////////////////////////////
+
+ int pausecount; ///< the number of times pause() has been invoked; -1 indicates unpauseable
+ JSFunction f = null; ///< the currently-executing JSFunction
+ JSScope scope; ///< the current top-level scope (LIFO stack via NEWSCOPE/OLDSCOPE)
+ Vec stack = new Vec(); ///< the object stack
+ int pc = 0; ///< the program counter
+
+ Interpreter(JSFunction f, boolean pauseable, JSArray args) {
+ stack.push(new Interpreter.CallMarker(this)); // the "root function returned" marker -- f==null
+ this.f = f;
+ this.pausecount = pauseable ? 0 : -1;
+ this.scope = new JSScope(f.parentScope);
+ stack.push(args);
+ }
+
+ /** this is the only synchronization point we need in order to be threadsafe */
+ synchronized Object resume() throws JSExn {
+ Thread t = Thread.currentThread();
+ Interpreter old = (Interpreter)threadToInterpreter.get(t);
+ threadToInterpreter.put(t, this);
+ try {
+ return run();
+ } finally {
+ if (old == null) threadToInterpreter.remove(t);
+ else threadToInterpreter.put(t, old);
+ }
+ }
+
+ static int getLine() {
+ Interpreter c = Interpreter.current();
+ return c == null || c.f == null || c.pc < 0 || c.pc >= c.f.size ? -1 : c.f.line[c.pc];
+ }
+
+ static String getSourceName() {
+ Interpreter c = Interpreter.current();
+ return c == null || c.f == null ? null : c.f.sourceName;
+ }
+
+ private static JSExn je(String s) { return new JSExn(getSourceName() + ":" + getLine() + " " + s); }
+
+ // FIXME: double check the trap logic
+ private Object run() throws JSExn {
+
+ // if pausecount changes after a get/put/call, we know we've been paused
+ final int initialPauseCount = pausecount;
+
+ OUTER: for(;; pc++) {
+ try {
+ if (f == null) return stack.pop();
+ int op = f.op[pc];
+ Object arg = f.arg[pc];
+ if(op == FINALLY_DONE) {
+ FinallyData fd = (FinallyData) stack.pop();
+ if(fd == null) continue OUTER; // NOP
+ if(fd.exn != null) throw fd.exn;
+ op = fd.op;
+ arg = fd.arg;
+ }
+ switch(op) {
+ case LITERAL: stack.push(arg); break;
+ case OBJECT: stack.push(new JS()); break;
+ case ARRAY: stack.push(new JSArray(JS.toNumber(arg).intValue())); break;
+ case DECLARE: scope.declare((String)(arg==null ? stack.peek() : arg)); if(arg != null) stack.push(arg); break;
+ case TOPSCOPE: stack.push(scope); break;
+ case JT: if (JS.toBoolean(stack.pop())) pc += JS.toNumber(arg).intValue() - 1; break;
+ case JF: if (!JS.toBoolean(stack.pop())) pc += JS.toNumber(arg).intValue() - 1; break;
+ case JMP: pc += JS.toNumber(arg).intValue() - 1; break;
+ case POP: stack.pop(); break;
+ case SWAP: {
+ int depth = (arg == null ? 1 : JS.toInt(arg));
+ Object save = stack.elementAt(stack.size() - 1);
+ for(int i=stack.size() - 1; i > stack.size() - 1 - depth; i--)
+ stack.setElementAt(stack.elementAt(i-1), i);
+ stack.setElementAt(save, stack.size() - depth - 1);
+ break; }
+ case DUP: stack.push(stack.peek()); break;
+ case NEWSCOPE: scope = new JSScope(scope); break;
+ case OLDSCOPE: scope = scope.getParentScope(); break;
+ case ASSERT:
+ if (JS.checkAssertions && !JS.toBoolean(stack.pop()))
+ throw je("ibex.assertion.failed" /*FEATURE: line number*/); break;
+ case BITNOT: stack.push(JS.N(~JS.toLong(stack.pop()))); break;
+ case BANG: stack.push(JS.B(!JS.toBoolean(stack.pop()))); break;
+ case NEWFUNCTION: stack.push(((JSFunction)arg)._cloneWithNewParentScope(scope)); break;
+ case LABEL: break;
+
+ case TYPEOF: {
+ Object o = stack.pop();
+ if (o == null) stack.push(null);
+ else if (o instanceof JS) stack.push("object");
+ else if (o instanceof String) stack.push("string");
+ else if (o instanceof Number) stack.push("number");
+ else if (o instanceof Boolean) stack.push("boolean");
+ else throw new Error("this should not happen");
+ break;
+ }
+
+ case PUSHKEYS: {
+ Object o = stack.peek();
+ Enumeration e = ((JS)o).keys();
+ JSArray a = new JSArray();
+ while(e.hasMoreElements()) a.addElement(e.nextElement());
+ stack.push(a);
+ break;
+ }
+
+ case LOOP:
+ stack.push(new LoopMarker(pc, pc > 0 && f.op[pc - 1] == LABEL ? (String)f.arg[pc - 1] : (String)null, scope));
+ stack.push(Boolean.TRUE);
+ break;
+
+ case BREAK:
+ case CONTINUE:
+ while(stack.size() > 0) {
+ Object o = stack.pop();
+ if (o instanceof CallMarker) je("break or continue not within a loop");
+ if (o instanceof TryMarker) {
+ if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going
+ stack.push(new FinallyData(op, arg));
+ scope = ((TryMarker)o).scope;
+ pc = ((TryMarker)o).finallyLoc - 1;
+ continue OUTER;
+ }
+ if (o instanceof LoopMarker) {
+ if (arg == null || arg.equals(((LoopMarker)o).label)) {
+ int loopInstructionLocation = ((LoopMarker)o).location;
+ int endOfLoop = ((Integer)f.arg[loopInstructionLocation]).intValue() + loopInstructionLocation;
+ scope = ((LoopMarker)o).scope;
+ if (op == CONTINUE) { stack.push(o); stack.push(Boolean.FALSE); }
+ pc = op == BREAK ? endOfLoop - 1 : loopInstructionLocation;
+ continue OUTER;
+ }
+ }
+ }
+ throw new Error("CONTINUE/BREAK invoked but couldn't find LoopMarker at " +
+ getSourceName() + ":" + getLine());
+
+ case TRY: {
+ int[] jmps = (int[]) arg;
+ // jmps[0] is how far away the catch block is, jmps[1] is how far away the finally block is
+ // each can be < 0 if the specified block does not exist
+ stack.push(new TryMarker(jmps[0] < 0 ? -1 : pc + jmps[0], jmps[1] < 0 ? -1 : pc + jmps[1], this));
+ break;
+ }
+
+ case RETURN: {
+ Object retval = stack.pop();
+ while(stack.size() > 0) {
+ Object o = stack.pop();
+ if (o instanceof TryMarker) {
+ if(((TryMarker)o).finallyLoc < 0) continue;
+ stack.push(retval);
+ stack.push(new FinallyData(RETURN));
+ scope = ((TryMarker)o).scope;
+ pc = ((TryMarker)o).finallyLoc - 1;
+ continue OUTER;
+ } else if (o instanceof CallMarker) {
+ if (scope instanceof Trap.TrapScope) { // handles return component of a read trap
+ Trap.TrapScope ts = (Trap.TrapScope)scope;
+ if (retval != null && retval instanceof Boolean && ((Boolean)retval).booleanValue())
+ ts.cascadeHappened = true;
+ if (!ts.cascadeHappened) {
+ ts.cascadeHappened = true;
+ Trap t = ts.t.next;
+ while (t != null && t.f.numFormalArgs == 0) t = t.next;
+ if (t == null) {
+ ((JS)ts.t.trapee).put(ts.t.name, ts.val);
+ if (pausecount > initialPauseCount) { pc++; return null; } // we were paused
+ } else {
+ stack.push(o);
+ JSArray args = new JSArray();
+ args.addElement(ts.val);
+ stack.push(args);
+ f = t.f;
+ scope = new Trap.TrapScope(f.parentScope, t, ts.val);
+ pc = -1;
+ continue OUTER;
+ }
+ }
+ }
+ scope = ((CallMarker)o).scope;
+ pc = ((CallMarker)o).pc - 1;
+ f = (JSFunction)((CallMarker)o).f;
+ stack.push(retval);
+ continue OUTER;
+ }
+ }
+ throw new Error("error: RETURN invoked but couldn't find a CallMarker!");
+ }
+
+ case PUT: {
+ Object val = stack.pop();
+ Object key = stack.pop();
+ Object target = stack.peek();
+ if (target == null)
+ throw je("tried to put a value to the " + key + " property on the null value");
+ if (!(target instanceof JS))
+ throw je("tried to put a value to the " + key + " property on a " + target.getClass().getName());
+ if (key == null)
+ throw je("tried to assign \"" + (val==null?"(null)":val.toString()) + "\" to the null key");
+
+ Trap t = null;
+ if (target instanceof JSScope && key.equals("cascade")) {
+ Trap.TrapScope ts = null;
+ JSScope p = (JSScope)target; // search the scope-path for the trap
+ if (target instanceof Trap.TrapScope) {
+ ts = (Trap.TrapScope)target;
+ }
+ else {
+ while (ts == null && p.getParentScope() != null) {
+ p = p.getParentScope();
+ if (p instanceof Trap.TrapScope) {
+ ts = (Trap.TrapScope)p;
+ }
+ }
+ }
+ t = ts.t.next;
+ ts.cascadeHappened = true;
+ while (t != null && t.f.numFormalArgs == 0) t = t.next;
+ if (t == null) { target = ts.t.trapee; key = ts.t.name; }
+
+ } else if (target instanceof Trap.TrapScope && key.equals(((Trap.TrapScope)target).t.name)) {
+ throw je("tried to put to " + key + " inside a trap it owns; use cascade instead");
+
+ } else if (target instanceof JS) {
+ if (target instanceof JSScope) {
+ JSScope p = (JSScope)target; // search the scope-path for the trap
+ t = p.getTrap(key);
+ while (t == null && p.getParentScope() != null) { p = p.getParentScope(); t = p.getTrap(key); }
+ } else {
+ t = ((JS)target).getTrap(key);
+ }
+ while (t != null && t.f.numFormalArgs == 0) t = t.next; // find the first write trap
+ }
+ if (t != null) {
+ stack.push(new CallMarker(this));
+ JSArray args = new JSArray();
+ args.addElement(val);
+ stack.push(args);
+ f = t.f;
+ scope = new Trap.TrapScope(f.parentScope, t, val);
+ pc = -1;
+ break;
+ }
+ ((JS)target).put(key, val);
+ if (pausecount > initialPauseCount) { pc++; return null; } // we were paused
+ stack.push(val);
+ break;
+ }
+
+ case GET:
+ case GET_PRESERVE: {
+ Object o, v;
+ if (op == GET) {
+ v = arg == null ? stack.pop() : arg;
+ o = stack.pop();
+ } else {
+ v = stack.pop();
+ o = stack.peek();
+ stack.push(v);
+ }
+ Object ret = null;
+ if (v == null) throw je("tried to get the null key from " + o);
+ if (o == null) throw je("tried to get property \"" + v + "\" from the null object");
+ if (o instanceof String || o instanceof Number || o instanceof Boolean) {
+ ret = getFromPrimitive(o,v);
+ stack.push(ret);
+ break;
+ } else if (o instanceof JS) {
+ Trap t = null;
+ if (o instanceof Trap.TrapScope && v.equals("cascade")) {
+ t = ((Trap.TrapScope)o).t.next;
+ while (t != null && t.f.numFormalArgs != 0) t = t.next;
+ if (t == null) { v = ((Trap.TrapScope)o).t.name; o = ((Trap.TrapScope)o).t.trapee; }
+
+ } else if (o instanceof JS) {
+ if (o instanceof JSScope) {
+ JSScope p = (JSScope)o; // search the scope-path for the trap
+ t = p.getTrap(v);
+ while (t == null && p.getParentScope() != null) { p = p.getParentScope(); t = p.getTrap(v); }
+ } else {
+ t = ((JS)o).getTrap(v);
+ }
+ while (t != null && t.f.numFormalArgs != 0) t = t.next; // get first read trap
+ }
+ if (t != null) {
+ stack.push(new CallMarker(this));
+ JSArray args = new JSArray();
+ stack.push(args);
+ f = t.f;
+ scope = new Trap.TrapScope(f.parentScope, t, null);
+ ((Trap.TrapScope)scope).cascadeHappened = true;
+ pc = -1;
+ break;
+ }
+ ret = ((JS)o).get(v);
+ if (ret == JS.METHOD) ret = new Stub((JS)o, v);
+ if (pausecount > initialPauseCount) { pc++; return null; } // we were paused
+ stack.push(ret);
+ break;
+ }
+ throw je("tried to get property " + v + " from a " + o.getClass().getName());
+ }
+
+ case CALL: case CALLMETHOD: {
+ int numArgs = JS.toInt(arg);
+ Object method = null;
+ Object ret = null;
+ Object object = stack.pop();
+
+ if (op == CALLMETHOD) {
+ if (object == JS.METHOD) {
+ method = stack.pop();
+ object = stack.pop();
+ } else if (object == null) {
+ Object name = stack.pop();
+ stack.pop();
+ throw new JSExn("function '"+name+"' not found");
+ } else {
+ stack.pop();
+ stack.pop();
+ }
+ }
+ Object[] rest = numArgs > 3 ? new Object[numArgs - 3] : null;
+ for(int i=numArgs - 1; i>2; i--) rest[i-3] = stack.pop();
+ Object a2 = numArgs <= 2 ? null : stack.pop();
+ Object a1 = numArgs <= 1 ? null : stack.pop();
+ Object a0 = numArgs <= 0 ? null : stack.pop();
+
+ if (object instanceof String || object instanceof Number || object instanceof Boolean) {
+ ret = callMethodOnPrimitive(object, method, a0, a1, a2, null, numArgs);
+
+ } else if (object instanceof JSFunction) {
+ // FIXME: use something similar to call0/call1/call2 here
+ JSArray arguments = new JSArray();
+ for(int i=0; i initialPauseCount) { pc++; return null; }
+ stack.push(ret);
+ break;
+ }
+
+ case THROW:
+ throw new JSExn(stack.pop(), stack, f, pc, scope);
+
+ /* FIXME
+ case MAKE_GRAMMAR: {
+ final Grammar r = (Grammar)arg;
+ final JSScope final_scope = scope;
+ Grammar r2 = new Grammar() {
+ public int match(String s, int start, Hash v, JSScope scope) throws JSExn {
+ return r.match(s, start, v, final_scope);
+ }
+ public int matchAndWrite(String s, int start, Hash v, JSScope scope, String key) throws JSExn {
+ return r.matchAndWrite(s, start, v, final_scope, key);
+ }
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ Hash v = new Hash();
+ r.matchAndWrite((String)a0, 0, v, final_scope, "foo");
+ return v.get("foo");
+ }
+ };
+ Object obj = stack.pop();
+ if (obj != null && obj instanceof Grammar) r2 = new Grammar.Alternative((Grammar)obj, r2);
+ stack.push(r2);
+ break;
+ }
+ */
+ case ADD_TRAP: case DEL_TRAP: {
+ Object val = stack.pop();
+ Object key = stack.pop();
+ Object obj = stack.peek();
+ // A trap addition/removal
+ JS js = obj instanceof JSScope ? ((JSScope)obj).top() : (JS) obj;
+ if(op == ADD_TRAP) js.addTrap(key, (JSFunction)val);
+ else js.delTrap(key, (JSFunction)val);
+ break;
+ }
+
+ case ASSIGN_SUB: case ASSIGN_ADD: {
+ Object val = stack.pop();
+ Object key = stack.pop();
+ Object obj = stack.peek();
+ // The following setup is VERY important. The generated bytecode depends on the stack
+ // being setup like this (top to bottom) KEY, OBJ, VAL, KEY, OBJ
+ stack.push(key);
+ stack.push(val);
+ stack.push(obj);
+ stack.push(key);
+ break;
+ }
+
+ case ADD: {
+ int count = ((Number)arg).intValue();
+ if(count < 2) throw new Error("this should never happen");
+ if(count == 2) {
+ // common case
+ Object right = stack.pop();
+ Object left = stack.pop();
+ if(left instanceof String || right instanceof String)
+ stack.push(JS.toString(left).concat(JS.toString(right)));
+ else stack.push(JS.N(JS.toDouble(left) + JS.toDouble(right)));
+ } else {
+ Object[] args = new Object[count];
+ while(--count >= 0) args[count] = stack.pop();
+ if(args[0] instanceof String) {
+ StringBuffer sb = new StringBuffer(64);
+ for(int i=0;i> JS.toLong(right))); break;
+ case URSH: stack.push(JS.N(JS.toLong(left) >>> JS.toLong(right))); break;
+
+ case LT: case LE: case GT: case GE: {
+ if (left == null) left = JS.N(0);
+ if (right == null) right = JS.N(0);
+ int result = 0;
+ if (left instanceof String || right instanceof String) {
+ result = left.toString().compareTo(right.toString());
+ } else {
+ result = (int)java.lang.Math.ceil(JS.toDouble(left) - JS.toDouble(right));
+ }
+ stack.push(JS.B((op == LT && result < 0) || (op == LE && result <= 0) ||
+ (op == GT && result > 0) || (op == GE && result >= 0)));
+ break;
+ }
+
+ case EQ:
+ case NE: {
+ Object l = left;
+ Object r = right;
+ boolean ret;
+ if (l == null) { Object tmp = r; r = l; l = tmp; }
+ if (l == null && r == null) ret = true;
+ else if (r == null) ret = false; // l != null, so its false
+ else if (l instanceof Boolean) ret = JS.B(JS.toBoolean(r)).equals(l);
+ else if (l instanceof Number) ret = JS.toNumber(r).doubleValue() == JS.toNumber(l).doubleValue();
+ else if (l instanceof String) ret = r != null && l.equals(r.toString());
+ else ret = l.equals(r);
+ stack.push(JS.B(op == EQ ? ret : !ret)); break;
+ }
+
+ default: throw new Error("unknown opcode " + op);
+ } }
+ }
+
+ } catch(JSExn e) {
+ while(stack.size() > 0) {
+ Object o = stack.pop();
+ if (o instanceof CatchMarker || o instanceof TryMarker) {
+ boolean inCatch = o instanceof CatchMarker;
+ if(inCatch) {
+ o = stack.pop();
+ if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going
+ }
+ if(!inCatch && ((TryMarker)o).catchLoc >= 0) {
+ // run the catch block, this will implicitly run the finally block, if it exists
+ stack.push(o);
+ stack.push(catchMarker);
+ stack.push(e.getObject());
+ f = ((TryMarker)o).f;
+ scope = ((TryMarker)o).scope;
+ pc = ((TryMarker)o).catchLoc - 1;
+ continue OUTER;
+ } else {
+ stack.push(new FinallyData(e));
+ f = ((TryMarker)o).f;
+ scope = ((TryMarker)o).scope;
+ pc = ((TryMarker)o).finallyLoc - 1;
+ continue OUTER;
+ }
+ }
+ }
+ throw e;
+ } // end try/catch
+ } // end for
+ }
+
+
+
+ // Markers //////////////////////////////////////////////////////////////////////
+
+ public static class CallMarker {
+ int pc;
+ JSScope scope;
+ JSFunction f;
+ public CallMarker(Interpreter cx) { pc = cx.pc + 1; scope = cx.scope; f = cx.f; }
+ }
+
+ public static class CatchMarker { }
+ private static CatchMarker catchMarker = new CatchMarker();
+
+ public static class LoopMarker {
+ public int location;
+ public String label;
+ public JSScope scope;
+ public LoopMarker(int location, String label, JSScope scope) {
+ this.location = location;
+ this.label = label;
+ this.scope = scope;
+ }
+ }
+ public static class TryMarker {
+ public int catchLoc;
+ public int finallyLoc;
+ public JSScope scope;
+ public JSFunction f;
+ public TryMarker(int catchLoc, int finallyLoc, Interpreter cx) {
+ this.catchLoc = catchLoc;
+ this.finallyLoc = finallyLoc;
+ this.scope = cx.scope;
+ this.f = cx.f;
+ }
+ }
+ public static class FinallyData {
+ public int op;
+ public Object arg;
+ public JSExn exn;
+ public FinallyData(int op) { this(op,null); }
+ public FinallyData(int op, Object arg) { this.op = op; this.arg = arg; }
+ public FinallyData(JSExn exn) { this.exn = exn; } // Just throw this exn
+ }
+
+
+ // Operations on Primitives //////////////////////////////////////////////////////////////////////
+
+ static Object callMethodOnPrimitive(Object o, Object method, Object arg0, Object arg1, Object arg2, Object[] rest, int alength) throws JSExn {
+ if (method == null || !(method instanceof String) || "".equals(method))
+ throw new JSExn("attempt to call a non-existant method on a primitive");
+
+ if (o instanceof Number) {
+final String ccSwitch0 = (String)(method); SUCCESS:do { switch(ccSwitch0.length()) {
+case 7: { switch(ccSwitch0.charAt(0)) { case 't': if ("toFixed".equals(ccSwitch0)) { if (true) do { throw new JSExn("toFixed() not implemented");
+ } while(false); break SUCCESS; } break; }; break; } case 8: { switch(ccSwitch0.charAt(0)) { case 't': if ("toString".equals(ccSwitch0)) { if (true) do { {
+int radix = alength >= 1 ? JS.toInt(arg0) : 10;
+return Long.toString(((Number)o).longValue(),radix);
+}
+ } while(false); break SUCCESS; } break; }; break; } case 11: { switch(ccSwitch0.charAt(0)) { case 't': if ("toPrecision".equals(ccSwitch0)) { if (true) do { throw new JSExn("toPrecision() not implemented");
+ } while(false); break SUCCESS; } break; }; break; } case 13: { switch(ccSwitch0.charAt(0)) { case 't': if ("toExponential".equals(ccSwitch0)) { if (true) do { throw new JSExn("toExponential() not implemented");
+ } while(false); break SUCCESS; } break; }; break; } } /* switch */ } while(false); /* OUTER */
+ } else if (o instanceof Boolean) {
+ // No methods for Booleans
+ throw new JSExn("attempt to call a method on a Boolean");
+ }
+
+ String s = JS.toString(o);
+ int slength = s.length();
+final String ccSwitch1 = (String)(method); SUCCESS:do { switch(ccSwitch1.length()) {
+case 5: { switch(ccSwitch1.charAt(0)) { case 'm': if ("match".equals(ccSwitch1)) { if (true) do { return JSRegexp.stringMatch(s,arg0);
+ } while(false); break SUCCESS; } break; case 's': { switch(ccSwitch1.charAt(1)) { case 'l': if ("slice".equals(ccSwitch1)) { if (true) do { {
+int a = alength >= 1 ? JS.toInt(arg0) : 0;
+int b = alength >= 2 ? JS.toInt(arg1) : slength;
+if (a < 0) a = slength + a;
+if (b < 0) b = slength + b;
+if (a < 0) a = 0;
+if (b < 0) b = 0;
+if (a > slength) a = slength;
+if (b > slength) b = slength;
+if (a > b) return "";
+return s.substring(a,b);
+}
+ } while(false); break SUCCESS; } break; case 'p': if ("split".equals(ccSwitch1)) { if (true) do { return JSRegexp.stringSplit(s,arg0,arg1,alength);
+ } while(false); break SUCCESS; } break; } break; } }; break; } case 6: { switch(ccSwitch1.charAt(0)) { case 'c': { switch(ccSwitch1.charAt(1)) { case 'h': if ("charAt".equals(ccSwitch1)) { if (true) do { {
+int p = alength >= 1 ? JS.toInt(arg0) : 0;
+if (p < 0 || p >= slength) return "";
+return s.substring(p,p+1);
+}
+ } while(false); break SUCCESS; } break; case 'o': if ("concat".equals(ccSwitch1)) { if (true) do { {
+StringBuffer sb = new StringBuffer(slength*2).append(s);
+for(int i=0;i= 1 ? JS.toInt(arg0) : 0;
+int len = alength >= 2 ? JS.toInt(arg1) : Integer.MAX_VALUE;
+if (start < 0) start = slength + start;
+if (start < 0) start = 0;
+if (len < 0) len = 0;
+if (len > slength - start) len = slength - start;
+if (len <= 0) return "";
+return s.substring(start,start+len);
+}
+ } while(false); break SUCCESS; } break; } break; } }; break; } case 7: { switch(ccSwitch1.charAt(0)) { case 'i': if ("indexOf".equals(ccSwitch1)) { if (true) do { {
+String search = alength >= 1 ? arg0.toString() : "null";
+int start = alength >= 2 ? JS.toInt(arg1) : 0;
+
+return JS.N(s.indexOf(search,start));
+}
+ } while(false); break SUCCESS; } break; case 'r': if ("replace".equals(ccSwitch1)) { if (true) do { return JSRegexp.stringReplace(s,arg0,arg1);
+ } while(false); break SUCCESS; } break; }; break; } case 8: { switch(ccSwitch1.charAt(0)) { case 't': if ("toString".equals(ccSwitch1)) { if (true) do { return s;
+ } while(false); break SUCCESS; } break; }; break; } case 9: { switch(ccSwitch1.charAt(0)) { case 's': if ("substring".equals(ccSwitch1)) { if (true) do { {
+int a = alength >= 1 ? JS.toInt(arg0) : 0;
+int b = alength >= 2 ? JS.toInt(arg1) : slength;
+if (a > slength) a = slength;
+if (b > slength) b = slength;
+if (a < 0) a = 0;
+if (b < 0) b = 0;
+if (a > b) { int tmp = a; a = b; b = tmp; }
+return s.substring(a,b);
+}
+ } while(false); break SUCCESS; } break; }; break; } case 10: { switch(ccSwitch1.charAt(0)) { case 'c': if ("charCodeAt".equals(ccSwitch1)) { if (true) do { {
+int p = alength >= 1 ? JS.toInt(arg0) : 0;
+if (p < 0 || p >= slength) return JS.N(Double.NaN);
+return JS.N(s.charAt(p));
+}
+ } while(false); break SUCCESS; } break; }; break; } case 11: { switch(ccSwitch1.charAt(0)) { case 'l': if ("lastIndexOf".equals(ccSwitch1)) { if (true) do { {
+String search = alength >= 1 ? arg0.toString() : "null";
+int start = alength >= 2 ? JS.toInt(arg1) : 0;
+
+return JS.N(s.lastIndexOf(search,start));
+}
+ } while(false); break SUCCESS; } break; case 't': { switch(ccSwitch1.charAt(1)) { case 'o': { switch(ccSwitch1.charAt(2)) { case 'L': if ("toLowerCase".equals(ccSwitch1)) { if (true) do { return s.toLowerCase();
+ } while(false); break SUCCESS; } break; case 'U': if ("toUpperCase".equals(ccSwitch1)) { if (true) do { return s.toUpperCase();
+ } while(false); break SUCCESS; } break; } break; } } break; } }; break; } } /* switch */ } while(false); /* OUTER */
+ throw new JSExn("Attempted to call non-existent method: " + method);
+ }
+
+ static Object getFromPrimitive(Object o, Object key) throws JSExn {
+ boolean returnJS = false;
+ if (o instanceof Boolean) {
+ throw new JSExn("Booleans do not have properties");
+ } else if (o instanceof Number) {
+ if (key.equals("toPrecision") || key.equals("toExponential") || key.equals("toFixed"))
+ returnJS = true;
+ }
+ if (!returnJS) {
+ // the string stuff applies to everything
+ String s = o.toString();
+
+ // this is sort of ugly, but this list should never change
+ // These should provide a complete (enough) implementation of the ECMA-262 String object
+
+final String ccSwitch2 = (String)(key); SUCCESS:do { switch(ccSwitch2.length()) {
+case 5: { switch(ccSwitch2.charAt(0)) { case 'm': if ("match".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; case 's': { switch(ccSwitch2.charAt(1)) { case 'l': if ("slice".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; case 'p': if ("split".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; } break; } }; break; } case 6: { switch(ccSwitch2.charAt(0)) { case 'c': { switch(ccSwitch2.charAt(1)) { case 'h': if ("charAt".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; case 'o': if ("concat".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; } break; } case 'l': if ("length".equals(ccSwitch2)) { if (true) do { return JS.N(s.length());
+ } while(false); break SUCCESS; } break; case 's': { switch(ccSwitch2.charAt(1)) { case 'e': if ("seatch".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; case 'u': if ("substr".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; } break; } }; break; } case 7: { switch(ccSwitch2.charAt(0)) { case 'i': if ("indexOf".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; case 'r': if ("replace".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; }; break; } case 8: { switch(ccSwitch2.charAt(0)) { case 't': if ("toString".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; }; break; } case 9: { switch(ccSwitch2.charAt(0)) { case 's': if ("substring".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; }; break; } case 10: { switch(ccSwitch2.charAt(0)) { case 'c': if ("charCodeAt".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; }; break; } case 11: { switch(ccSwitch2.charAt(0)) { case 'l': if ("lastIndexOf".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; case 't': { switch(ccSwitch2.charAt(1)) { case 'o': { switch(ccSwitch2.charAt(2)) { case 'L': if ("toLowerCase".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; case 'U': if ("toUpperCase".equals(ccSwitch2)) { if (true) do { returnJS = true; break;
+ } while(false); break SUCCESS; } break; } break; } } break; } }; break; } } /* switch */ } while(false); /* OUTER */
+ }
+ if (returnJS) {
+ final Object target = o;
+ final String method = key.toString();
+ return new JS() {
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ if (nargs > 2) throw new JSExn("cannot call that method with that many arguments");
+ return callMethodOnPrimitive(target, method, a0, a1, a2, rest, nargs);
+ }
+ };
+ }
+ return null;
+ }
+
+ private static class Stub extends JS {
+ private Object method;
+ JS obj;
+ public Stub(JS obj, Object method) { this.obj = obj; this.method = method; }
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ return ((JS)obj).callMethod(method, a0, a1, a2, rest, nargs);
+ }
+ }
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/js/JS.java b/upstream/org.ibex.core/build/java/org/ibex/js/JS.java
new file mode 100644
index 0000000..7a78623
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/js/JS.java
@@ -0,0 +1,236 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+
+/** The minimum set of functionality required for objects which are manipulated by JavaScript */
+public class JS extends org.ibex.util.BalancedTree {
+
+ public static boolean checkAssertions = false;
+
+ public static final Object METHOD = new Object();
+ public final JS unclone() { return _unclone(); }
+ public Enumeration keys() throws JSExn { return entries == null ? emptyEnumeration : entries.keys(); }
+ public Object get(Object key) throws JSExn { return entries == null ? null : entries.get(key, null); }
+ public void put(Object key, Object val) throws JSExn { (entries==null?entries=new Hash():entries).put(key,null,val); }
+ public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ throw new JSExn("attempted to call the null value (method "+method+")");
+ }
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ throw new JSExn("you cannot call this object (class=" + this.getClass().getName() +")");
+ }
+
+ JS _unclone() { return this; }
+ public static class Cloneable extends JS {
+ public Object jsclone() throws JSExn {
+ return new Clone(this);
+ }
+ }
+
+ public static class Clone extends JS.Cloneable {
+ protected JS.Cloneable clonee = null;
+ JS _unclone() { return clonee.unclone(); }
+ public JS.Cloneable getClonee() { return clonee; }
+ public Clone(JS.Cloneable clonee) { this.clonee = clonee; }
+ public boolean equals(Object o) {
+ if (!(o instanceof JS)) return false;
+ return unclone() == ((JS)o).unclone();
+ }
+ public Enumeration keys() throws JSExn { return clonee.keys(); }
+ public Object get(Object key) throws JSExn { return clonee.get(key); }
+ public void put(Object key, Object val) throws JSExn { clonee.put(key, val); }
+ public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ return clonee.callMethod(method, a0, a1, a2, rest, nargs);
+ }
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ return clonee.call(a0, a1, a2, rest, nargs);
+ }
+ }
+
+ // Static Interpreter Control Methods ///////////////////////////////////////////////////////////////
+
+ /** log a message with the current JavaScript sourceName/line */
+ public static void log(Object message) { info(message); }
+ public static void debug(Object message) { Log.debug(Interpreter.getSourceName() + ":" + Interpreter.getLine(), message); }
+ public static void info(Object message) { Log.info(Interpreter.getSourceName() + ":" + Interpreter.getLine(), message); }
+ public static void warn(Object message) { Log.warn(Interpreter.getSourceName() + ":" + Interpreter.getLine(), message); }
+ public static void error(Object message) { Log.error(Interpreter.getSourceName() + ":" + Interpreter.getLine(), message); }
+
+ public static class NotPauseableException extends Exception { NotPauseableException() { } }
+
+ /** returns a callback which will restart the context; expects a value to be pushed onto the stack when unpaused */
+ public static UnpauseCallback pause() throws NotPauseableException {
+ Interpreter i = Interpreter.current();
+ if (i.pausecount == -1) throw new NotPauseableException();
+ i.pausecount++;
+ return new JS.UnpauseCallback(i);
+ }
+
+ public static class UnpauseCallback implements Task {
+ Interpreter i;
+ UnpauseCallback(Interpreter i) { this.i = i; }
+ public void perform() throws JSExn { unpause(null); }
+ public void unpause(Object o) throws JSExn {
+ // FIXME: if o instanceof JSExn, throw it into the JSworld
+ i.stack.push(o);
+ i.resume();
+ }
+ }
+
+
+
+ // Static Helper Methods ///////////////////////////////////////////////////////////////////////////////////
+
+ /** coerce an object to a Boolean */
+ public static boolean toBoolean(Object o) {
+ if (o == null) return false;
+ if (o instanceof Boolean) return ((Boolean)o).booleanValue();
+ if (o instanceof Long) return ((Long)o).longValue() != 0;
+ if (o instanceof Integer) return ((Integer)o).intValue() != 0;
+ if (o instanceof Number) {
+ double d = ((Number) o).doubleValue();
+ // NOTE: d == d is a test for NaN. It should be faster than Double.isNaN()
+ return d != 0.0 && d == d;
+ }
+ if (o instanceof String) return ((String)o).length() != 0;
+ return true;
+ }
+
+ /** coerce an object to a Long */
+ public static long toLong(Object o) { return toNumber(o).longValue(); }
+
+ /** coerce an object to an Int */
+ public static int toInt(Object o) { return toNumber(o).intValue(); }
+
+ /** coerce an object to a Double */
+ public static double toDouble(Object o) { return toNumber(o).doubleValue(); }
+
+ /** coerce an object to a Number */
+ public static Number toNumber(Object o) {
+ if (o == null) return ZERO;
+ if (o instanceof Number) return ((Number)o);
+
+ // NOTE: There are about 3 pages of rules in ecma262 about string to number conversions
+ // We aren't even close to following all those rules. We probably never will be.
+ if (o instanceof String) try { return N((String)o); } catch (NumberFormatException e) { return N(Double.NaN); }
+ if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? N(1) : ZERO;
+ throw new Error("toNumber() got object of type " + o.getClass().getName() + " which we don't know how to handle");
+ }
+
+ /** coerce an object to a String */
+ public static String toString(Object o) {
+ if(o == null) return "null";
+ if(o instanceof String) return (String) o;
+ if(o instanceof Integer || o instanceof Long || o instanceof Boolean) return o.toString();
+ if(o instanceof JSArray) return o.toString();
+ if(o instanceof JSDate) return o.toString();
+ if(o instanceof Double || o instanceof Float) {
+ double d = ((Number)o).doubleValue();
+ if((int)d == d) return Integer.toString((int)d);
+ return o.toString();
+ }
+ throw new RuntimeException("can't coerce "+o+" [" + o.getClass().getName() + "] to type String.");
+ }
+
+ // Instance Methods ////////////////////////////////////////////////////////////////////
+
+ public static final Integer ZERO = new Integer(0);
+
+ // this gets around a wierd fluke in the Java type checking rules for ?..:
+ public static final Object T = Boolean.TRUE;
+ public static final Object F = Boolean.FALSE;
+
+ public static final Boolean B(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; }
+ public static final Boolean B(int i) { return i==0 ? Boolean.FALSE : Boolean.TRUE; }
+ public static final Number N(String s) { return s.indexOf('.') == -1 ? N(Integer.parseInt(s)) : new Double(s); }
+ public static final Number N(double d) { return (int)d == d ? N((int)d) : new Double(d); }
+ public static final Number N(long l) { return N((int)l); }
+
+ private static final Integer[] smallIntCache = new Integer[65535 / 4];
+ private static final Integer[] largeIntCache = new Integer[65535 / 4];
+ public static final Number N(int i) {
+ Integer ret = null;
+ int idx = i + smallIntCache.length / 2;
+ if (idx < smallIntCache.length && idx > 0) {
+ ret = smallIntCache[idx];
+ if (ret != null) return ret;
+ }
+ else ret = largeIntCache[Math.abs(idx % largeIntCache.length)];
+ if (ret == null || ret.intValue() != i) {
+ ret = new Integer(i);
+ if (idx < smallIntCache.length && idx > 0) smallIntCache[idx] = ret;
+ else largeIntCache[Math.abs(idx % largeIntCache.length)] = ret;
+ }
+ return ret;
+ }
+
+ private static Enumeration emptyEnumeration = new Enumeration() {
+ public boolean hasMoreElements() { return false; }
+ public Object nextElement() { throw new NoSuchElementException(); }
+ };
+
+ private Hash entries = null;
+
+ public static JS fromReader(String sourceName, int firstLine, Reader sourceCode) throws IOException {
+ return JSFunction._fromReader(sourceName, firstLine, sourceCode);
+ }
+
+ // HACK: caller can't know if the argument is a JSFunction or not...
+ public static JS cloneWithNewParentScope(JS j, JSScope s) {
+ return ((JSFunction)j)._cloneWithNewParentScope(s);
+ }
+
+
+ // Trap support //////////////////////////////////////////////////////////////////////////////
+
+ /** override and return true to allow placing traps on this object.
+ * if isRead true, this is a read trap, otherwise write trap
+ **/
+ protected boolean isTrappable(Object name, boolean isRead) { return true; }
+
+ /** performs a put, triggering traps if present; traps are run in an unpauseable interpreter */
+ public void putAndTriggerTraps(Object key, Object value) throws JSExn {
+ Trap t = getTrap(key);
+ if (t != null) t.invoke(value);
+ else put(key, value);
+ }
+
+ /** performs a get, triggering traps if present; traps are run in an unpauseable interpreter */
+ public Object getAndTriggerTraps(Object key) throws JSExn {
+ Trap t = getTrap(key);
+ if (t != null) return t.invoke();
+ else return get(key);
+ }
+
+ /** retrieve a trap from the entries hash */
+ protected final Trap getTrap(Object key) {
+ return entries == null ? null : (Trap)entries.get(key, Trap.class);
+ }
+
+ /** retrieve a trap from the entries hash */
+ protected final void putTrap(Object key, Trap value) {
+ if (entries == null) entries = new Hash();
+ entries.put(key, Trap.class, value);
+ }
+
+ /** adds a trap, avoiding duplicates */
+ protected final void addTrap(Object name, JSFunction f) throws JSExn {
+ if (f.numFormalArgs > 1) throw new JSExn("traps must take either one argument (write) or no arguments (read)");
+ boolean isRead = f.numFormalArgs == 0;
+ if (!isTrappable(name, isRead)) throw new JSExn("not allowed "+(isRead?"read":"write")+" trap on property: "+name);
+ for(Trap t = getTrap(name); t != null; t = t.next) if (t.f == f) return;
+ putTrap(name, new Trap(this, name.toString(), f, (Trap)getTrap(name)));
+ }
+
+ /** deletes a trap, if present */
+ protected final void delTrap(Object name, JSFunction f) {
+ Trap t = (Trap)getTrap(name);
+ if (t == null) return;
+ if (t.f == f) { putTrap(t.name, t.next); return; }
+ for(; t.next != null; t = t.next) if (t.next.f == f) { t.next = t.next.next; return; }
+ }
+
+
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/js/JSArray.java b/upstream/org.ibex.core/build/java/org/ibex/js/JSArray.java
new file mode 100644
index 0000000..8a70773
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/js/JSArray.java
@@ -0,0 +1,262 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+import org.ibex.util.*;
+import java.util.*;
+
+/** A JavaScript JSArray */
+public class JSArray extends JS {
+ private static final Object NULL = new Object();
+
+ public JSArray() { }
+ public JSArray(int size) { setSize(size); }
+
+ private static int intVal(Object o) {
+ if (o instanceof Number) {
+ int intVal = ((Number)o).intValue();
+ if (intVal == ((Number)o).doubleValue()) return intVal;
+ return Integer.MIN_VALUE;
+ }
+ if (!(o instanceof String)) return Integer.MIN_VALUE;
+ String s = (String)o;
+ for(int i=0; i '9') return Integer.MIN_VALUE;
+ return Integer.parseInt(s);
+ }
+
+ public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+final String ccSwitch0 = (String)(method); SUCCESS:do { switch(ccSwitch0.length()) {
+case 3: { switch(ccSwitch0.charAt(0)) { case 'p': if ("pop".equals(ccSwitch0)) { if (true) do { {
+int oldSize = size();
+if(oldSize == 0) return null;
+return removeElementAt(oldSize-1);
+}
+ } while(false); break SUCCESS; } break; }; break; } case 4: { switch(ccSwitch0.charAt(0)) { case 'j': if ("join".equals(ccSwitch0)) { if (true) do {
+return join(nargs == 0 ? "," : JS.toString(a0));
+ } while(false); break SUCCESS; } break; case 'p': if ("push".equals(ccSwitch0)) { if (true) do { {
+int oldSize = size();
+for(int i=0; i= size()) return null;
+ return elementAt(i);
+ }
+final String ccSwitch1 = (String)(key); SUCCESS:do { switch(ccSwitch1.length()) {
+case 3: { switch(ccSwitch1.charAt(0)) { case 'p': if ("pop".equals(ccSwitch1)) { if (true) do { return METHOD;
+ } while(false); break SUCCESS; } break; }; break; } case 4: { switch(ccSwitch1.charAt(0)) { case 'j': if ("join".equals(ccSwitch1)) { if (true) do { return METHOD;
+ } while(false); break SUCCESS; } break; case 'p': if ("push".equals(ccSwitch1)) { if (true) do { return METHOD;
+ } while(false); break SUCCESS; } break; case 's': if ("sort".equals(ccSwitch1)) { if (true) do { return METHOD;
+ } while(false); break SUCCESS; } break; }; break; } case 5: { switch(ccSwitch1.charAt(0)) { case 's': { switch(ccSwitch1.charAt(1)) { case 'h': if ("shift".equals(ccSwitch1)) { if (true) do { return METHOD;
+ } while(false); break SUCCESS; } break; case 'l': if ("slice".equals(ccSwitch1)) { if (true) do { return METHOD;
+ } while(false); break SUCCESS; } break; } break; } }; break; } case 6: { switch(ccSwitch1.charAt(0)) { case 'l': if ("length".equals(ccSwitch1)) { if (true) do { return N(size());
+ } while(false); break SUCCESS; } break; case 's': if ("splice".equals(ccSwitch1)) { if (true) do { return METHOD;
+ } while(false); break SUCCESS; } break; }; break; } case 7: { switch(ccSwitch1.charAt(0)) { case 'r': if ("reverse".equals(ccSwitch1)) { if (true) do { return METHOD;
+ } while(false); break SUCCESS; } break; case 'u': if ("unshift".equals(ccSwitch1)) { if (true) do { return METHOD;
+ } while(false); break SUCCESS; } break; }; break; } case 8: { switch(ccSwitch1.charAt(0)) { case 't': if ("toString".equals(ccSwitch1)) { if (true) do { return METHOD;
+ } while(false); break SUCCESS; } break; }; break; } } /* switch */ } while(false); /* OUTER */
+ return super.get(key);
+ }
+
+ public void put(Object key, Object val) throws JSExn {
+ if (key.equals("length")) setSize(toInt(val));
+ int i = intVal(key);
+ if (i == Integer.MIN_VALUE)
+ super.put(key, val);
+ else {
+ int oldSize = size();
+ if(i < oldSize) {
+ setElementAt(val,i);
+ } else {
+ if(i > oldSize) setSize(i);
+ insertElementAt(val,i);
+ }
+ }
+ }
+
+ public Enumeration keys() {
+ return new Enumeration() {
+ private int n = size();
+ public boolean hasMoreElements() { return n > 0; }
+ public Object nextElement() {
+ if(n == 0) throw new NoSuchElementException();
+ return new Integer(--n);
+ }
+ };
+ }
+
+ public final void setSize(int newSize) {
+ // FEATURE: This could be done a lot more efficiently in BalancedTree
+ int oldSize = size();
+ for(int i=oldSize;i=newSize;i--) removeElementAt(i);
+ }
+
+ public final int length() { return size(); }
+ public final Object elementAt(int i) {
+ if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
+ Object o = getNode(i);
+ return o == NULL ? null : o;
+ }
+ public final void addElement(Object o) {
+ insertNode(size(),o==null ? NULL : o);
+ }
+ public final void setElementAt(Object o, int i) {
+ if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
+ replaceNode(i,o==null ? NULL : o);
+ }
+ public final void insertElementAt(Object o, int i) {
+ if(i < 0 || i > size()) throw new ArrayIndexOutOfBoundsException(i);
+ insertNode(i,o==null ? NULL : o);
+ }
+ public final Object removeElementAt(int i) {
+ if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
+ Object o = deleteNode(i);
+ return o == NULL ? null : o;
+ }
+
+ public final int size() { return treeSize(); }
+ public String typeName() { return "array"; }
+
+ private Object join(String sep) {
+ int length = size();
+ if(length == 0) return "";
+ StringBuffer sb = new StringBuffer(64);
+ int i=0;
+ while(true) {
+ Object o = elementAt(i);
+ if(o != null) sb.append(JS.toString(o));
+ if(++i == length) break;
+ sb.append(sep);
+ }
+ return sb.toString();
+ }
+
+ // FEATURE: Implement this more efficiently
+ private Object reverse() {
+ int size = size();
+ if(size < 2) return this;
+ Vec vec = toVec();
+ clear();
+ for(int i=size-1,j=0;i>=0;i--,j++) insertElementAt(vec.elementAt(i),j);
+ return this;
+ }
+
+ private Object slice(int start, int end) {
+ int length = length();
+ if(start < 0) start = length+start;
+ if(end < 0) end = length+end;
+ if(start < 0) start = 0;
+ if(end < 0) end = 0;
+ if(start > length) start = length;
+ if(end > length) end = length;
+ JSArray a = new JSArray(end-start);
+ for(int i=0;i oldLength) start = oldLength;
+ if(deleteCount < 0) deleteCount = 0;
+ if(deleteCount > oldLength-start) deleteCount = oldLength-start;
+ int newLength = oldLength - deleteCount + newCount;
+ int lengthChange = newLength - oldLength;
+ JSArray ret = new JSArray(deleteCount);
+ for(int i=0;i 0) {
+ setSize(newLength);
+ for(int i=newLength-1;i>=start+newCount;i--)
+ setElementAt(elementAt(i-lengthChange),i);
+ } else if(lengthChange < 0) {
+ for(int i=start+newCount;i lo) {
+ mid = (hi + lo) / 2;
+ if (TimeFromYear(mid) > t) {
+ hi = mid - 1;
+ } else {
+ if (TimeFromYear(mid) <= t) {
+ int temp = mid + 1;
+ if (TimeFromYear(temp) > t) {
+ return mid;
+ }
+ lo = mid + 1;
+ }
+ }
+ }
+ return lo;
+ }
+
+ private static boolean InLeapYear(double t) {
+ return DaysInYear(YearFromTime(t)) == 366;
+ }
+
+ private static int DayWithinYear(double t) {
+ int year = YearFromTime(t);
+ return (int) (Day(t) - DayFromYear(year));
+ }
+ /*
+ * The following array contains the day of year for the first day of
+ * each month, where index 0 is January, and day 0 is January 1.
+ */
+
+ private static double DayFromMonth(int m, boolean leap) {
+ int day = m * 30;
+
+ if (m >= 7) { day += m / 2 - 1; }
+ else if (m >= 2) { day += (m - 1) / 2 - 1; }
+ else { day += m; }
+
+ if (leap && m >= 2) { ++day; }
+
+ return day;
+ }
+
+ private static int MonthFromTime(double t) {
+ int d, step;
+
+ d = DayWithinYear(t);
+
+ if (d < (step = 31))
+ return 0;
+
+ // Originally coded as step += (InLeapYear(t) ? 29 : 28);
+ // but some jits always returned 28!
+ if (InLeapYear(t))
+ step += 29;
+ else
+ step += 28;
+
+ if (d < step)
+ return 1;
+ if (d < (step += 31))
+ return 2;
+ if (d < (step += 30))
+ return 3;
+ if (d < (step += 31))
+ return 4;
+ if (d < (step += 30))
+ return 5;
+ if (d < (step += 31))
+ return 6;
+ if (d < (step += 31))
+ return 7;
+ if (d < (step += 30))
+ return 8;
+ if (d < (step += 31))
+ return 9;
+ if (d < (step += 30))
+ return 10;
+ return 11;
+ }
+
+ private static int DateFromTime(double t) {
+ int d, step, next;
+
+ d = DayWithinYear(t);
+ if (d <= (next = 30))
+ return d + 1;
+ step = next;
+
+ // Originally coded as next += (InLeapYear(t) ? 29 : 28);
+ // but some jits always returned 28!
+ if (InLeapYear(t))
+ next += 29;
+ else
+ next += 28;
+
+ if (d <= next)
+ return d - step;
+ step = next;
+ if (d <= (next += 31))
+ return d - step;
+ step = next;
+ if (d <= (next += 30))
+ return d - step;
+ step = next;
+ if (d <= (next += 31))
+ return d - step;
+ step = next;
+ if (d <= (next += 30))
+ return d - step;
+ step = next;
+ if (d <= (next += 31))
+ return d - step;
+ step = next;
+ if (d <= (next += 31))
+ return d - step;
+ step = next;
+ if (d <= (next += 30))
+ return d - step;
+ step = next;
+ if (d <= (next += 31))
+ return d - step;
+ step = next;
+ if (d <= (next += 30))
+ return d - step;
+ step = next;
+
+ return d - step;
+ }
+
+ private static int WeekDay(double t) {
+ double result;
+ result = Day(t) + 4;
+ result = result % 7;
+ if (result < 0)
+ result += 7;
+ return (int) result;
+ }
+
+ private static double Now() {
+ return (double) System.currentTimeMillis();
+ }
+
+ /* Should be possible to determine the need for this dynamically
+ * if we go with the workaround... I'm not using it now, because I
+ * can't think of any clean way to make toLocaleString() and the
+ * time zone (comment) in toString match the generated string
+ * values. Currently it's wrong-but-consistent in all but the
+ * most recent betas of the JRE - seems to work in 1.1.7.
+ */
+ private final static boolean TZO_WORKAROUND = false;
+ private static double DaylightSavingTA(double t) {
+ if (!TZO_WORKAROUND) {
+ java.util.Date date = new java.util.Date((long) t);
+ if (thisTimeZone.inDaylightTime(date))
+ return msPerHour;
+ else
+ return 0;
+ } else {
+ /* Use getOffset if inDaylightTime() is broken, because it
+ * seems to work acceptably. We don't switch over to it
+ * entirely, because it requires (expensive) exploded date arguments,
+ * and the api makes it impossible to handle dst
+ * changeovers cleanly.
+ */
+
+ // Hardcode the assumption that the changeover always
+ // happens at 2:00 AM:
+ t += LocalTZA + (HourFromTime(t) <= 2 ? msPerHour : 0);
+
+ int year = YearFromTime(t);
+ double offset = thisTimeZone.getOffset(year > 0 ? 1 : 0,
+ year,
+ MonthFromTime(t),
+ DateFromTime(t),
+ WeekDay(t),
+ (int)TimeWithinDay(t));
+
+ if ((offset - LocalTZA) != 0)
+ return msPerHour;
+ else
+ return 0;
+ // return offset - LocalTZA;
+ }
+ }
+
+ private static double LocalTime(double t) {
+ return t + LocalTZA + DaylightSavingTA(t);
+ }
+
+ public static double internalUTC(double t) {
+ return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
+ }
+
+ private static int HourFromTime(double t) {
+ double result;
+ result = java.lang.Math.floor(t / msPerHour) % HoursPerDay;
+ if (result < 0)
+ result += HoursPerDay;
+ return (int) result;
+ }
+
+ private static int MinFromTime(double t) {
+ double result;
+ result = java.lang.Math.floor(t / msPerMinute) % MinutesPerHour;
+ if (result < 0)
+ result += MinutesPerHour;
+ return (int) result;
+ }
+
+ private static int SecFromTime(double t) {
+ double result;
+ result = java.lang.Math.floor(t / msPerSecond) % SecondsPerMinute;
+ if (result < 0)
+ result += SecondsPerMinute;
+ return (int) result;
+ }
+
+ private static int msFromTime(double t) {
+ double result;
+ result = t % msPerSecond;
+ if (result < 0)
+ result += msPerSecond;
+ return (int) result;
+ }
+
+ private static double MakeTime(double hour, double min,
+ double sec, double ms)
+ {
+ return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec)
+ * msPerSecond + ms;
+ }
+
+ private static double MakeDay(double year, double month, double date) {
+ double result;
+ boolean leap;
+ double yearday;
+ double monthday;
+
+ year += java.lang.Math.floor(month / 12);
+
+ month = month % 12;
+ if (month < 0)
+ month += 12;
+
+ leap = (DaysInYear((int) year) == 366);
+
+ yearday = java.lang.Math.floor(TimeFromYear(year) / msPerDay);
+ monthday = DayFromMonth((int) month, leap);
+
+ result = yearday
+ + monthday
+ + date - 1;
+ return result;
+ }
+
+ private static double MakeDate(double day, double time) {
+ return day * msPerDay + time;
+ }
+
+ private static double TimeClip(double d) {
+ if (d != d ||
+ d == Double.POSITIVE_INFINITY ||
+ d == Double.NEGATIVE_INFINITY ||
+ java.lang.Math.abs(d) > HalfTimeDomain)
+ {
+ return Double.NaN;
+ }
+ if (d > 0.0)
+ return java.lang.Math.floor(d + 0.);
+ else
+ return java.lang.Math.ceil(d + 0.);
+ }
+
+ /* end of ECMA helper functions */
+
+ /* find UTC time from given date... no 1900 correction! */
+ public static double date_msecFromDate(double year, double mon,
+ double mday, double hour,
+ double min, double sec,
+ double msec)
+ {
+ double day;
+ double time;
+ double result;
+
+ day = MakeDay(year, mon, mday);
+ time = MakeTime(hour, min, sec, msec);
+ result = MakeDate(day, time);
+ return result;
+ }
+
+
+ private static final int MAXARGS = 7;
+ private static double jsStaticJSFunction_UTC(Object[] args) {
+ double array[] = new double[MAXARGS];
+ int loop;
+ double d;
+
+ for (loop = 0; loop < MAXARGS; loop++) {
+ if (loop < args.length) {
+ d = _toNumber(args[loop]);
+ if (d != d || Double.isInfinite(d)) {
+ return Double.NaN;
+ }
+ array[loop] = toDouble(args[loop]);
+ } else {
+ array[loop] = 0;
+ }
+ }
+
+ /* adjust 2-digit years into the 20th century */
+ if (array[0] >= 0 && array[0] <= 99)
+ array[0] += 1900;
+
+ /* if we got a 0 for 'date' (which is out of range)
+ * pretend it's a 1. (So Date.UTC(1972, 5) works) */
+ if (array[2] < 1)
+ array[2] = 1;
+
+ d = date_msecFromDate(array[0], array[1], array[2],
+ array[3], array[4], array[5], array[6]);
+ d = TimeClip(d);
+ return d;
+ // return N(d);
+ }
+
+ /*
+ * Use ported code from jsdate.c rather than the locale-specific
+ * date-parsing code from Java, to keep js and rhino consistent.
+ * Is this the right strategy?
+ */
+
+ /* for use by date_parse */
+
+ /* replace this with byte arrays? Cheaper? */
+ private static String wtb[] = {
+ "am", "pm",
+ "monday", "tuesday", "wednesday", "thursday", "friday",
+ "saturday", "sunday",
+ "january", "february", "march", "april", "may", "june",
+ "july", "august", "september", "october", "november", "december",
+ "gmt", "ut", "utc", "est", "edt", "cst", "cdt",
+ "mst", "mdt", "pst", "pdt"
+ /* time zone table needs to be expanded */
+ };
+
+ private static int ttb[] = {
+ -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
+ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 10000 + 0, 10000 + 0, 10000 + 0, /* UT/UTC */
+ 10000 + 5 * 60, 10000 + 4 * 60, /* EDT */
+ 10000 + 6 * 60, 10000 + 5 * 60,
+ 10000 + 7 * 60, 10000 + 6 * 60,
+ 10000 + 8 * 60, 10000 + 7 * 60
+ };
+
+ /* helper for date_parse */
+ private static boolean date_regionMatches(String s1, int s1off,
+ String s2, int s2off,
+ int count)
+ {
+ boolean result = false;
+ /* return true if matches, otherwise, false */
+ int s1len = s1.length();
+ int s2len = s2.length();
+
+ while (count > 0 && s1off < s1len && s2off < s2len) {
+ if (Character.toLowerCase(s1.charAt(s1off)) !=
+ Character.toLowerCase(s2.charAt(s2off)))
+ break;
+ s1off++;
+ s2off++;
+ count--;
+ }
+
+ if (count == 0) {
+ result = true;
+ }
+ return result;
+ }
+
+ private static double date_parseString(String s) {
+ double msec;
+
+ int year = -1;
+ int mon = -1;
+ int mday = -1;
+ int hour = -1;
+ int min = -1;
+ int sec = -1;
+ char c = 0;
+ char si = 0;
+ int i = 0;
+ int n = -1;
+ double tzoffset = -1;
+ char prevc = 0;
+ int limit = 0;
+ boolean seenplusminus = false;
+
+ if (s == null) // ??? Will s be null?
+ return Double.NaN;
+ limit = s.length();
+ while (i < limit) {
+ c = s.charAt(i);
+ i++;
+ if (c <= ' ' || c == ',' || c == '-') {
+ if (i < limit) {
+ si = s.charAt(i);
+ if (c == '-' && '0' <= si && si <= '9') {
+ prevc = c;
+ }
+ }
+ continue;
+ }
+ if (c == '(') { /* comments) */
+ int depth = 1;
+ while (i < limit) {
+ c = s.charAt(i);
+ i++;
+ if (c == '(')
+ depth++;
+ else if (c == ')')
+ if (--depth <= 0)
+ break;
+ }
+ continue;
+ }
+ if ('0' <= c && c <= '9') {
+ n = c - '0';
+ while (i < limit && '0' <= (c = s.charAt(i)) && c <= '9') {
+ n = n * 10 + c - '0';
+ i++;
+ }
+
+ /* allow TZA before the year, so
+ * 'Wed Nov 05 21:49:11 GMT-0800 1997'
+ * works */
+
+ /* uses of seenplusminus allow : in TZA, so Java
+ * no-timezone style of GMT+4:30 works
+ */
+ if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
+ /* make ':' case below change tzoffset */
+ seenplusminus = true;
+
+ /* offset */
+ if (n < 24)
+ n = n * 60; /* EG. "GMT-3" */
+ else
+ n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
+ if (prevc == '+') /* plus means east of GMT */
+ n = -n;
+ if (tzoffset != 0 && tzoffset != -1)
+ return Double.NaN;
+ tzoffset = n;
+ } else if (n >= 70 ||
+ (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
+ if (year >= 0)
+ return Double.NaN;
+ else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
+ year = n < 100 ? n + 1900 : n;
+ else
+ return Double.NaN;
+ } else if (c == ':') {
+ if (hour < 0)
+ hour = /*byte*/ n;
+ else if (min < 0)
+ min = /*byte*/ n;
+ else
+ return Double.NaN;
+ } else if (c == '/') {
+ if (mon < 0)
+ mon = /*byte*/ n-1;
+ else if (mday < 0)
+ mday = /*byte*/ n;
+ else
+ return Double.NaN;
+ } else if (i < limit && c != ',' && c > ' ' && c != '-') {
+ return Double.NaN;
+ } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
+ if (tzoffset < 0)
+ tzoffset -= n;
+ else
+ tzoffset += n;
+ } else if (hour >= 0 && min < 0) {
+ min = /*byte*/ n;
+ } else if (min >= 0 && sec < 0) {
+ sec = /*byte*/ n;
+ } else if (mday < 0) {
+ mday = /*byte*/ n;
+ } else {
+ return Double.NaN;
+ }
+ prevc = 0;
+ } else if (c == '/' || c == ':' || c == '+' || c == '-') {
+ prevc = c;
+ } else {
+ int st = i - 1;
+ int k;
+ while (i < limit) {
+ c = s.charAt(i);
+ if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
+ break;
+ i++;
+ }
+ if (i <= st + 1)
+ return Double.NaN;
+ for (k = wtb.length; --k >= 0;)
+ if (date_regionMatches(wtb[k], 0, s, st, i-st)) {
+ int action = ttb[k];
+ if (action != 0) {
+ if (action < 0) {
+ /*
+ * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
+ * 12:30, instead of blindly adding 12 if PM.
+ */
+ if (hour > 12 || hour < 0) {
+ return Double.NaN;
+ } else {
+ if (action == -1 && hour == 12) { // am
+ hour = 0;
+ } else if (action == -2 && hour != 12) {// pm
+ hour += 12;
+ }
+ }
+ } else if (action <= 13) { /* month! */
+ if (mon < 0) {
+ mon = /*byte*/ (action - 2);
+ } else {
+ return Double.NaN;
+ }
+ } else {
+ tzoffset = action - 10000;
+ }
+ }
+ break;
+ }
+ if (k < 0)
+ return Double.NaN;
+ prevc = 0;
+ }
+ }
+ if (year < 0 || mon < 0 || mday < 0)
+ return Double.NaN;
+ if (sec < 0)
+ sec = 0;
+ if (min < 0)
+ min = 0;
+ if (hour < 0)
+ hour = 0;
+ if (tzoffset == -1) { /* no time zone specified, have to use local */
+ double time;
+ time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
+ return internalUTC(time);
+ }
+
+ msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
+ msec += tzoffset * msPerMinute;
+ return msec;
+ }
+
+ private static double jsStaticJSFunction_parse(String s) {
+ return date_parseString(s);
+ }
+
+ private static final int FORMATSPEC_FULL = 0;
+ private static final int FORMATSPEC_DATE = 1;
+ private static final int FORMATSPEC_TIME = 2;
+
+ private static String date_format(double t, int format) {
+ if (t != t)
+ return NaN_date_str;
+
+ StringBuffer result = new StringBuffer(60);
+ double local = LocalTime(t);
+
+ /* offset from GMT in minutes. The offset includes daylight savings,
+ if it applies. */
+ int minutes = (int) java.lang.Math.floor((LocalTZA + DaylightSavingTA(t))
+ / msPerMinute);
+ /* map 510 minutes to 0830 hours */
+ int offset = (minutes / 60) * 100 + minutes % 60;
+
+ String dateStr = Integer.toString(DateFromTime(local));
+ String hourStr = Integer.toString(HourFromTime(local));
+ String minStr = Integer.toString(MinFromTime(local));
+ String secStr = Integer.toString(SecFromTime(local));
+ String offsetStr = Integer.toString(offset > 0 ? offset : -offset);
+ int year = YearFromTime(local);
+ String yearStr = Integer.toString(year > 0 ? year : -year);
+
+ /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */
+ /* Tue Oct 31 2000 */
+ /* 09:41:40 GMT-0800 (PST) */
+
+ if (format != FORMATSPEC_TIME) {
+ result.append(days[WeekDay(local)]);
+ result.append(' ');
+ result.append(months[MonthFromTime(local)]);
+ if (dateStr.length() == 1)
+ result.append(" 0");
+ else
+ result.append(' ');
+ result.append(dateStr);
+ result.append(' ');
+ }
+
+ if (format != FORMATSPEC_DATE) {
+ if (hourStr.length() == 1)
+ result.append('0');
+ result.append(hourStr);
+ if (minStr.length() == 1)
+ result.append(":0");
+ else
+ result.append(':');
+ result.append(minStr);
+ if (secStr.length() == 1)
+ result.append(":0");
+ else
+ result.append(':');
+ result.append(secStr);
+ if (offset > 0)
+ result.append(" GMT+");
+ else
+ result.append(" GMT-");
+ for (int i = offsetStr.length(); i < 4; i++)
+ result.append('0');
+ result.append(offsetStr);
+
+ if (timeZoneFormatter == null)
+ timeZoneFormatter = new java.text.SimpleDateFormat("zzz");
+
+ if (timeZoneFormatter != null) {
+ result.append(" (");
+ java.util.Date date = new java.util.Date((long) t);
+ result.append(timeZoneFormatter.format(date));
+ result.append(')');
+ }
+ if (format != FORMATSPEC_TIME)
+ result.append(' ');
+ }
+
+ if (format != FORMATSPEC_TIME) {
+ if (year < 0)
+ result.append('-');
+ for (int i = yearStr.length(); i < 4; i++)
+ result.append('0');
+ result.append(yearStr);
+ }
+
+ return result.toString();
+ }
+
+ private static double _toNumber(Object o) { return JS.toDouble(o); }
+ private static double _toNumber(Object[] o, int index) { return JS.toDouble(o[index]); }
+ private static double toDouble(double d) { return d; }
+
+ public JSDate(Object a0, Object a1, Object a2, Object[] rest, int nargs) {
+
+ JSDate obj = this;
+ switch (nargs) {
+ case 0: {
+ obj.date = Now();
+ return;
+ }
+ case 1: {
+ double date;
+ if (a0 instanceof JS)
+ a0 = ((JS) a0).toString();
+ if (!(a0 instanceof String)) {
+ // if it's not a string, use it as a millisecond date
+ date = _toNumber(a0);
+ } else {
+ // it's a string; parse it.
+ String str = (String) a0;
+ date = date_parseString(str);
+ }
+ obj.date = TimeClip(date);
+ return;
+ }
+ default: {
+ // multiple arguments; year, month, day etc.
+ double array[] = new double[MAXARGS];
+ array[0] = toDouble(a0);
+ array[1] = toDouble(a1);
+ if (nargs >= 2) array[2] = toDouble(a2);
+ for(int i=0; i= 0 && array[0] <= 99)
+ array[0] += 1900;
+
+ /* if we got a 0 for 'date' (which is out of range)
+ * pretend it's a 1 */
+ if (array[2] < 1)
+ array[2] = 1;
+
+ double day = MakeDay(array[0], array[1], array[2]);
+ double time = MakeTime(array[3], array[4], array[5], array[6]);
+ time = MakeDate(day, time);
+ time = internalUTC(time);
+ obj.date = TimeClip(time);
+
+ return;
+ }
+ }
+ }
+
+ /* constants for toString, toUTCString */
+ private static String NaN_date_str = "Invalid Date";
+
+ private static String[] days = {
+ "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
+ };
+
+ private static String[] months = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+
+ private static String toLocale_helper(double t,
+ java.text.DateFormat formatter)
+ {
+ if (t != t)
+ return NaN_date_str;
+
+ java.util.Date tempdate = new java.util.Date((long) t);
+ return formatter.format(tempdate);
+ }
+
+ private static String toLocaleString(double date) {
+ if (localeDateTimeFormatter == null)
+ localeDateTimeFormatter =
+ DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
+
+ return toLocale_helper(date, localeDateTimeFormatter);
+ }
+
+ private static String toLocaleTimeString(double date) {
+ if (localeTimeFormatter == null)
+ localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG);
+
+ return toLocale_helper(date, localeTimeFormatter);
+ }
+
+ private static String toLocaleDateString(double date) {
+ if (localeDateFormatter == null)
+ localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG);
+
+ return toLocale_helper(date, localeDateFormatter);
+ }
+
+ private static String toUTCString(double date) {
+ StringBuffer result = new StringBuffer(60);
+
+ String dateStr = Integer.toString(DateFromTime(date));
+ String hourStr = Integer.toString(HourFromTime(date));
+ String minStr = Integer.toString(MinFromTime(date));
+ String secStr = Integer.toString(SecFromTime(date));
+ int year = YearFromTime(date);
+ String yearStr = Integer.toString(year > 0 ? year : -year);
+
+ result.append(days[WeekDay(date)]);
+ result.append(", ");
+ if (dateStr.length() == 1)
+ result.append('0');
+ result.append(dateStr);
+ result.append(' ');
+ result.append(months[MonthFromTime(date)]);
+ if (year < 0)
+ result.append(" -");
+ else
+ result.append(' ');
+ int i;
+ for (i = yearStr.length(); i < 4; i++)
+ result.append('0');
+ result.append(yearStr);
+
+ if (hourStr.length() == 1)
+ result.append(" 0");
+ else
+ result.append(' ');
+ result.append(hourStr);
+ if (minStr.length() == 1)
+ result.append(":0");
+ else
+ result.append(':');
+ result.append(minStr);
+ if (secStr.length() == 1)
+ result.append(":0");
+ else
+ result.append(':');
+ result.append(secStr);
+
+ result.append(" GMT");
+ return result.toString();
+ }
+
+ private static double getYear(double date) {
+ int result = YearFromTime(LocalTime(date));
+ result -= 1900;
+ return result;
+ }
+
+ private static double getTimezoneOffset(double date) {
+ return (date - LocalTime(date)) / msPerMinute;
+ }
+
+ public double setTime(double time) {
+ this.date = TimeClip(time);
+ return this.date;
+ }
+
+ private double makeTime(Object[] args, int maxargs, boolean local) {
+ int i;
+ double conv[] = new double[4];
+ double hour, min, sec, msec;
+ double lorutime; /* Local or UTC version of date */
+
+ double time;
+ double result;
+
+ double date = this.date;
+
+ /* just return NaN if the date is already NaN */
+ if (date != date)
+ return date;
+
+ /* Satisfy the ECMA rule that if a function is called with
+ * fewer arguments than the specified formal arguments, the
+ * remaining arguments are set to undefined. Seems like all
+ * the Date.setWhatever functions in ECMA are only varargs
+ * beyond the first argument; this should be set to undefined
+ * if it's not given. This means that "d = new Date();
+ * d.setMilliseconds()" returns NaN. Blech.
+ */
+ if (args.length == 0)
+ args = new Object[] { null };
+
+ for (i = 0; i < args.length && i < maxargs; i++) {
+ conv[i] = _toNumber(args[i]);
+
+ // limit checks that happen in MakeTime in ECMA.
+ if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
+ this.date = Double.NaN;
+ return this.date;
+ }
+ conv[i] = toDouble(conv[i]);
+ }
+
+ if (local)
+ lorutime = LocalTime(date);
+ else
+ lorutime = date;
+
+ i = 0;
+ int stop = args.length;
+
+ if (maxargs >= 4 && i < stop)
+ hour = conv[i++];
+ else
+ hour = HourFromTime(lorutime);
+
+ if (maxargs >= 3 && i < stop)
+ min = conv[i++];
+ else
+ min = MinFromTime(lorutime);
+
+ if (maxargs >= 2 && i < stop)
+ sec = conv[i++];
+ else
+ sec = SecFromTime(lorutime);
+
+ if (maxargs >= 1 && i < stop)
+ msec = conv[i++];
+ else
+ msec = msFromTime(lorutime);
+
+ time = MakeTime(hour, min, sec, msec);
+ result = MakeDate(Day(lorutime), time);
+
+ if (local)
+ result = internalUTC(result);
+ date = TimeClip(result);
+
+ this.date = date;
+ return date;
+ }
+
+ private double setHours(Object[] args) {
+ return makeTime(args, 4, true);
+ }
+
+ private double setUTCHours(Object[] args) {
+ return makeTime(args, 4, false);
+ }
+
+ private double makeDate(Object[] args, int maxargs, boolean local) {
+ int i;
+ double conv[] = new double[3];
+ double year, month, day;
+ double lorutime; /* local or UTC version of date */
+ double result;
+
+ double date = this.date;
+
+ /* See arg padding comment in makeTime.*/
+ if (args.length == 0)
+ args = new Object[] { null };
+
+ for (i = 0; i < args.length && i < maxargs; i++) {
+ conv[i] = _toNumber(args[i]);
+
+ // limit checks that happen in MakeDate in ECMA.
+ if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
+ this.date = Double.NaN;
+ return this.date;
+ }
+ conv[i] = toDouble(conv[i]);
+ }
+
+ /* return NaN if date is NaN and we're not setting the year,
+ * If we are, use 0 as the time. */
+ if (date != date) {
+ if (args.length < 3) {
+ return Double.NaN;
+ } else {
+ lorutime = 0;
+ }
+ } else {
+ if (local)
+ lorutime = LocalTime(date);
+ else
+ lorutime = date;
+ }
+
+ i = 0;
+ int stop = args.length;
+
+ if (maxargs >= 3 && i < stop)
+ year = conv[i++];
+ else
+ year = YearFromTime(lorutime);
+
+ if (maxargs >= 2 && i < stop)
+ month = conv[i++];
+ else
+ month = MonthFromTime(lorutime);
+
+ if (maxargs >= 1 && i < stop)
+ day = conv[i++];
+ else
+ day = DateFromTime(lorutime);
+
+ day = MakeDay(year, month, day); /* day within year */
+ result = MakeDate(day, TimeWithinDay(lorutime));
+
+ if (local)
+ result = internalUTC(result);
+
+ date = TimeClip(result);
+
+ this.date = date;
+ return date;
+ }
+
+ private double setYear(double year) {
+ double day, result;
+ if (year != year || Double.isInfinite(year)) {
+ this.date = Double.NaN;
+ return this.date;
+ }
+
+ if (this.date != this.date) {
+ this.date = 0;
+ } else {
+ this.date = LocalTime(this.date);
+ }
+
+ if (year >= 0 && year <= 99)
+ year += 1900;
+
+ day = MakeDay(year, MonthFromTime(this.date), DateFromTime(this.date));
+ result = MakeDate(day, TimeWithinDay(this.date));
+ result = internalUTC(result);
+
+ this.date = TimeClip(result);
+ return this.date;
+ }
+
+
+ // private static final int
+ // Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6
+// #/string_id_map#
+
+ /* cached values */
+ private static java.util.TimeZone thisTimeZone;
+ private static double LocalTZA;
+ private static java.text.DateFormat timeZoneFormatter;
+ private static java.text.DateFormat localeDateTimeFormatter;
+ private static java.text.DateFormat localeDateFormatter;
+ private static java.text.DateFormat localeTimeFormatter;
+
+ private double date;
+
+ public long getRawTime() { return (long)this.date; }
+}
+
+
diff --git a/upstream/org.ibex.core/build/java/org/ibex/js/JSExn.java b/upstream/org.ibex.core/build/java/org/ibex/js/JSExn.java
new file mode 100644
index 0000000..b39d6c8
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/js/JSExn.java
@@ -0,0 +1,55 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+import org.ibex.util.*;
+import java.io.*;
+
+/** An exception which can be thrown and caught by JavaScript code */
+public class JSExn extends Exception {
+ private Vec backtrace = new Vec();
+ private Object js = null;
+ public JSExn(Object js) {
+ this.js = js;
+ if (Interpreter.current() != null)
+ fill(Interpreter.current().stack, Interpreter.current().f, Interpreter.current().pc, Interpreter.current().scope);
+ }
+ public JSExn(Object js, Vec stack, JSFunction f, int pc, JSScope scope) { this.js = js; fill(stack, f, pc, scope); }
+ private void fill(Vec stack, JSFunction f, int pc, JSScope scope) {
+ addBacktrace(f.sourceName + ":" + f.line[pc]);
+ if (scope != null && scope instanceof Trap.TrapScope)
+ addBacktrace("trap on property \"" + ((Trap.TrapScope)scope).t.name + "\"");
+ for(int i=stack.size()-1; i>=0; i--) {
+ Object element = stack.elementAt(i);
+ if (element instanceof Interpreter.CallMarker) {
+ Interpreter.CallMarker cm = (Interpreter.CallMarker)element;
+ if (cm.f != null)
+ addBacktrace(cm.f.sourceName + ":" + cm.f.line[cm.pc-1]);
+ if (cm.scope != null && cm.scope instanceof Trap.TrapScope)
+ addBacktrace("trap on property \"" + ((Trap.TrapScope)cm.scope).t.name + "\"");
+ }
+ }
+ }
+ public void printStackTrace() { printStackTrace(System.err); }
+ public void printStackTrace(PrintWriter pw) {
+ for(int i=0; iunpauseable context. */
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ JSArray args = new JSArray();
+ if (nargs > 0) args.addElement(a0);
+ if (nargs > 1) args.addElement(a1);
+ if (nargs > 2) args.addElement(a2);
+ for(int i=3; i= s.length()) { lastIndex = 0; return null; }
+REMatch match = re.getMatch(s,start);
+if(global) lastIndex = match == null ? s.length() : match.getEndIndex();
+return match == null ? null : matchToExecResult(match,re,s);
+}
+ } while(false); break SUCCESS; } break; case 't': if ("test".equals(ccSwitch0)) { if (true) do { {
+String s = (String)a0;
+if (!global) return B(re.getMatch(s) != null);
+int start = global ? lastIndex : 0;
+if(start < 0 || start >= s.length()) { lastIndex = 0; return null; }
+REMatch match = re.getMatch(s,start);
+lastIndex = match != null ? s.length() : match.getEndIndex();
+return B(match != null);
+}
+ } while(false); break SUCCESS; } break; }; break; } case 8: { switch(ccSwitch0.charAt(0)) { case 't': if ("toString".equals(ccSwitch0)) { if (true) do { return toString(a0);
+ } while(false); break SUCCESS; } break; }; break; } case 11: { switch(ccSwitch0.charAt(0)) { case 's': if ("stringMatch".equals(ccSwitch0)) { if (true) do { return stringMatch(a0,a1);
+ } while(false); break SUCCESS; } break; }; break; } case 12: { switch(ccSwitch0.charAt(0)) { case 's': if ("stringSearch".equals(ccSwitch0)) { if (true) do { return stringSearch(a0,a1);
+ } while(false); break SUCCESS; } break; }; break; } } /* switch */ } while(false); /* OUTER */
+ break;
+ }
+ case 2: {
+final String ccSwitch1 = (String)(method); SUCCESS:do { switch(ccSwitch1.length()) {
+case 13: { switch(ccSwitch1.charAt(0)) { case 's': if ("stringReplace".equals(ccSwitch1)) { if (true) do { return stringReplace(a0, a1,a2);
+ } while(false); break SUCCESS; } break; }; break; } } /* switch */ } while(false); /* OUTER */
+ break;
+ }
+ }
+ return super.callMethod(method, a0, a1, a2, rest, nargs);
+ }
+
+ public Object get(Object key) throws JSExn {
+final String ccSwitch2 = (String)(key); SUCCESS:do { switch(ccSwitch2.length()) {
+case 4: { switch(ccSwitch2.charAt(0)) { case 'e': if ("exec".equals(ccSwitch2)) { if (true) do { return METHOD;
+ } while(false); break SUCCESS; } break; case 't': if ("test".equals(ccSwitch2)) { if (true) do { return METHOD;
+ } while(false); break SUCCESS; } break; }; break; } case 8: { switch(ccSwitch2.charAt(0)) { case 't': if ("toString".equals(ccSwitch2)) { if (true) do { return METHOD;
+ } while(false); break SUCCESS; } break; }; break; } case 9: { switch(ccSwitch2.charAt(0)) { case 'l': if ("lastIndex".equals(ccSwitch2)) { if (true) do { return N(lastIndex);
+ } while(false); break SUCCESS; } break; }; break; } } /* switch */ } while(false); /* OUTER */
+ return super.get(key);
+ }
+
+ public void put(Object key, Object value) throws JSExn {
+ if(key.equals("lastIndex")) lastIndex = JS.toNumber(value).intValue();
+ super.put(key,value);
+ }
+
+ private static Object matchToExecResult(REMatch match, RE re, String s) {
+ try {
+ JS ret = new JS();
+ ret.put("index", N(match.getStartIndex()));
+ ret.put("input",s);
+ int n = re.getNumSubs();
+ ret.put("length", N(n+1));
+ ret.put("0",match.toString());
+ for(int i=1;i<=n;i++) ret.put(Integer.toString(i),match.toString(i));
+ return ret;
+ } catch (JSExn e) {
+ throw new Error("this should never happen");
+ }
+ }
+
+ public String toString() {
+ try {
+ StringBuffer sb = new StringBuffer();
+ sb.append('/');
+ sb.append(get("source"));
+ sb.append('/');
+ if(global) sb.append('g');
+ if(Boolean.TRUE.equals(get("ignoreCase"))) sb.append('i');
+ if(Boolean.TRUE.equals(get("multiline"))) sb.append('m');
+ return sb.toString();
+ } catch (JSExn e) {
+ throw new Error("this should never happen");
+ }
+ }
+
+ public static Object stringMatch(Object o, Object arg0) throws JSExn {
+ String s = o.toString();
+ RE re;
+ JSRegexp regexp = null;
+ if(arg0 instanceof JSRegexp) {
+ regexp = (JSRegexp) arg0;
+ re = regexp.re;
+ } else {
+ re = newRE(arg0.toString(),0);
+ }
+
+ if(regexp == null) {
+ REMatch match = re.getMatch(s);
+ return matchToExecResult(match,re,s);
+ }
+ if(!regexp.global) return regexp.callMethod("exec", s, null, null, null, 1);
+
+ JSArray ret = new JSArray();
+ REMatch[] matches = re.getAllMatches(s);
+ for(int i=0;i 0 ? matches[matches.length-1].getEndIndex() : s.length();
+ return ret;
+ }
+
+ public static Object stringSearch(Object o, Object arg0) throws JSExn {
+ String s = o.toString();
+ RE re = arg0 instanceof JSRegexp ? ((JSRegexp)arg0).re : newRE(arg0.toString(),0);
+ REMatch match = re.getMatch(s);
+ return match == null ? N(-1) : N(match.getStartIndex());
+ }
+
+ public static Object stringReplace(Object o, Object arg0, Object arg1) throws JSExn {
+ String s = o.toString();
+ RE re;
+ JSFunction replaceFunc = null;
+ String replaceString = null;
+ JSRegexp regexp = null;
+ if(arg0 instanceof JSRegexp) {
+ regexp = (JSRegexp) arg0;
+ re = regexp.re;
+ } else {
+ re = newRE(arg0.toString(),0);
+ }
+ if(arg1 instanceof JSFunction)
+ replaceFunc = (JSFunction) arg1;
+ else
+ replaceString = JS.toString(arg1.toString());
+ REMatch[] matches;
+ if(regexp != null && regexp.global) {
+ matches = re.getAllMatches(s);
+ if(regexp != null) {
+ if(matches.length > 0)
+ regexp.lastIndex = matches[matches.length-1].getEndIndex();
+ else
+ regexp.lastIndex = s.length();
+ }
+ } else {
+ REMatch match = re.getMatch(s);
+ if(match != null)
+ matches = new REMatch[]{ match };
+ else
+ matches = new REMatch[0];
+ }
+
+ StringBuffer sb = new StringBuffer(s.length());
+ int pos = 0;
+ char[] sa = s.toCharArray();
+ for(int i=0;i= '0' && c2 <= '9') {
+ n = (c - '0') * 10 + (c2 - '0');
+ i++;
+ } else {
+ n = c - '0';
+ }
+ if(n > 0)
+ sb.append(match.toString(n));
+ break;
+ case '$':
+ sb.append('$'); break;
+ case '&':
+ sb.append(match.toString()); break;
+ case '`':
+ sb.append(source.substring(0,match.getStartIndex())); break;
+ case '\'':
+ sb.append(source.substring(match.getEndIndex())); break;
+ default:
+ sb.append('$');
+ sb.append(c);
+ }
+ }
+ if(i < s.length()) sb.append(s.charAt(i));
+ return sb.toString();
+ }
+
+
+ public static Object stringSplit(String s, Object arg0, Object arg1, int nargs) {
+ int limit = nargs < 2 ? Integer.MAX_VALUE : JS.toInt(arg1);
+ if(limit < 0) limit = Integer.MAX_VALUE;
+ if(limit == 0) return new JSArray();
+
+ RE re = null;
+ JSRegexp regexp = null;
+ String sep = null;
+ JSArray ret = new JSArray();
+ int p = 0;
+
+ if(arg0 instanceof JSRegexp) {
+ regexp = (JSRegexp) arg0;
+ re = regexp.re;
+ } else {
+ sep = arg0.toString();
+ }
+
+ // special case this for speed. additionally, the code below doesn't properly handle
+ // zero length strings
+ if(sep != null && sep.length()==0) {
+ int len = s.length();
+ for(int i=0;i 36)) return NaN;
+ while(start < length && Character.isWhitespace(s.charAt(start))) start++;
+ if((length >= start+1) && (s.charAt(start) == '+' || s.charAt(start) == '-')) {
+ sign = s.charAt(start) == '+' ? 1 : -1;
+ start++;
+ }
+ if(radix == 0 && length >= start+1 && s.charAt(start) == '0') {
+ start++;
+ if(length >= start+1 && (s.charAt(start) == 'x' || s.charAt(start) == 'X')) {
+ start++;
+ radix = 16;
+ } else {
+ radix = 8;
+ if(length == start || Character.digit(s.charAt(start+1),8)==-1) return JS.ZERO;
+ }
+ }
+ if(radix == 0) radix = 10;
+ if(length == start || Character.digit(s.charAt(start),radix) == -1) return NaN;
+ // try the fast way first
+ try {
+ String s2 = start == 0 ? s : s.substring(start);
+ return JS.N(sign*Integer.parseInt(s2,radix));
+ } catch(NumberFormatException e) { }
+ // fall through to a slower but emca-compliant method
+ for(int i=start;iregardless of pushbacks */
+ protected int mostRecentlyReadToken;
+
+ /** if the token just parsed was a NUMBER, this is the numeric value */
+ protected Number number = null;
+
+ /** if the token just parsed was a NAME or STRING, this is the string value */
+ protected String string = null;
+
+ /** the line number of the most recently lexed token */
+ protected int line = 0;
+
+ /** the line number of the most recently parsed token */
+ protected int parserLine = 0;
+
+ /** the column number of the current token */
+ protected int col = 0;
+
+ /** the name of the source code file being lexed */
+ protected String sourceName;
+
+ private SmartReader in;
+ public Lexer(Reader r, String sourceName, int line) throws IOException {
+ this.sourceName = sourceName;
+ this.line = line;
+ this.parserLine = line;
+ in = new SmartReader(r);
+ }
+
+
+ // Predicates ///////////////////////////////////////////////////////////////////////
+
+ private static boolean isAlpha(int c) { return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); }
+ private static boolean isDigit(int c) { return (c >= '0' && c <= '9'); }
+ private static int xDigitToInt(int c) {
+ if ('0' <= c && c <= '9') return c - '0';
+ else if ('a' <= c && c <= 'f') return c - ('a' - 10);
+ else if ('A' <= c && c <= 'F') return c - ('A' - 10);
+ else return -1;
+ }
+
+
+ // Token Subtype Handlers /////////////////////////////////////////////////////////
+
+ private int getKeyword(String name) throws IOException {
+final String ccSwitch0 = (String)(name); SUCCESS:do { switch(ccSwitch0.length()) {
+case 2: { switch(ccSwitch0.charAt(0)) { case 'd': if ("do".equals(ccSwitch0)) { if (true) do { return DO;
+ } while(false); break SUCCESS; } break; case 'g': if ("gt".equals(ccSwitch0)) { if (true) do { return GT;
+ } while(false); break SUCCESS; } break; case 'i': { switch(ccSwitch0.charAt(1)) { case 'f': if ("if".equals(ccSwitch0)) { if (true) do { return IF;
+ } while(false); break SUCCESS; } break; case 'n': if ("in".equals(ccSwitch0)) { if (true) do { return IN;
+ } while(false); break SUCCESS; } break; } break; } case 'l': if ("lt".equals(ccSwitch0)) { if (true) do { return LT;
+ } while(false); break SUCCESS; } break; case 'o': if ("or".equals(ccSwitch0)) { if (true) do { return OR;
+ } while(false); break SUCCESS; } break; }; break; } case 3: { switch(ccSwitch0.charAt(0)) { case 'a': if ("and".equals(ccSwitch0)) { if (true) do { return AND;
+ } while(false); break SUCCESS; } break; case 'f': if ("for".equals(ccSwitch0)) { if (true) do { return FOR;
+ } while(false); break SUCCESS; } break; case 'i': if ("int".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'n': if ("new".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 't': if ("try".equals(ccSwitch0)) { if (true) do { return TRY;
+ } while(false); break SUCCESS; } break; case 'v': if ("var".equals(ccSwitch0)) { if (true) do { return VAR;
+ } while(false); break SUCCESS; } break; }; break; } case 4: { switch(ccSwitch0.charAt(0)) { case 'b': if ("byte".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'c': { switch(ccSwitch0.charAt(1)) { case 'a': if ("case".equals(ccSwitch0)) { if (true) do { return CASE;
+ } while(false); break SUCCESS; } break; case 'h': if ("char".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; } break; } case 'e': { switch(ccSwitch0.charAt(1)) { case 'l': if ("else".equals(ccSwitch0)) { if (true) do { return ELSE;
+ } while(false); break SUCCESS; } break; case 'n': if ("enum".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; } break; } case 'g': if ("goto".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'l': if ("long".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'n': if ("null".equals(ccSwitch0)) { if (true) do { return NULL;
+ } while(false); break SUCCESS; } break; case 't': if ("true".equals(ccSwitch0)) { if (true) do { return TRUE;
+ } while(false); break SUCCESS; } break; case 'v': if ("void".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'w': if ("with".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; }; break; } case 5: { switch(ccSwitch0.charAt(0)) { case 'b': if ("break".equals(ccSwitch0)) { if (true) do { return BREAK;
+ } while(false); break SUCCESS; } break; case 'c': { switch(ccSwitch0.charAt(1)) { case 'a': if ("catch".equals(ccSwitch0)) { if (true) do { return CATCH;
+ } while(false); break SUCCESS; } break; case 'l': if ("class".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'o': if ("const".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; } break; } case 'f': { switch(ccSwitch0.charAt(1)) { case 'a': if ("false".equals(ccSwitch0)) { if (true) do { return FALSE;
+ } while(false); break SUCCESS; } break; case 'i': if ("final".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; } break; } case 's': if ("super".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 't': if ("throw".equals(ccSwitch0)) { if (true) do { return THROW;
+ } while(false); break SUCCESS; } break; case 'w': if ("while".equals(ccSwitch0)) { if (true) do { return WHILE;
+ } while(false); break SUCCESS; } break; }; break; } case 6: { switch(ccSwitch0.charAt(0)) { case 'a': if ("assert".equals(ccSwitch0)) { if (true) do { return ASSERT;
+ } while(false); break SUCCESS; } break; case 'd': { switch(ccSwitch0.charAt(1)) { case 'e': if ("delete".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'o': if ("double".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; } break; } case 'p': if ("public".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'r': if ("return".equals(ccSwitch0)) { if (true) do { return RETURN;
+ } while(false); break SUCCESS; } break; case 's': if ("switch".equals(ccSwitch0)) { if (true) do { return SWITCH;
+ } while(false); break SUCCESS; } break; case 't': { switch(ccSwitch0.charAt(1)) { case 'h': if ("throws".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'y': if ("typeof".equals(ccSwitch0)) { if (true) do { return TYPEOF;
+ } while(false); break SUCCESS; } break; } break; } }; break; } case 7: { switch(ccSwitch0.charAt(0)) { case 'b': if ("boolean".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'd': if ("default".equals(ccSwitch0)) { if (true) do { return DEFAULT;
+ } while(false); break SUCCESS; } break; case 'e': if ("extends".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'f': if ("finally".equals(ccSwitch0)) { if (true) do { return FINALLY;
+ } while(false); break SUCCESS; } break; case 'p': { switch(ccSwitch0.charAt(1)) { case 'a': if ("package".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'r': if ("private".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; } break; } }; break; } case 8: { switch(ccSwitch0.charAt(0)) { case 'a': if ("abstract".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'c': if ("continue".equals(ccSwitch0)) { if (true) do { return CONTINUE;
+ } while(false); break SUCCESS; } break; case 'd': if ("debugger".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'f': if ("function".equals(ccSwitch0)) { if (true) do { return FUNCTION;
+ } while(false); break SUCCESS; } break; case 'v': if ("volatile".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; }; break; } case 9: { switch(ccSwitch0.charAt(0)) { case 'i': if ("interface".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'p': if ("protected".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 't': if ("transient".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; }; break; } case 10: { switch(ccSwitch0.charAt(0)) { case 'i': { switch(ccSwitch0.charAt(1)) { case 'm': if ("implements".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; case 'n': if ("instanceof".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; } break; } }; break; } case 12: { switch(ccSwitch0.charAt(0)) { case 's': if ("synchronized".equals(ccSwitch0)) { if (true) do { return RESERVED;
+ } while(false); break SUCCESS; } break; }; break; } } /* switch */ } while(false); /* OUTER */
+ return -1;
+ }
+
+ private int getIdentifier(int c) throws IOException {
+ in.startString();
+ while (Character.isJavaIdentifierPart((char)(c = in.read())));
+ in.unread();
+ String str = in.getString();
+ int result = getKeyword(str);
+ if (result == RESERVED) throw new LexerException("The reserved word \"" + str + "\" is not permitted in Ibex scripts");
+ if (result != -1) return result;
+ this.string = str.intern();
+ return NAME;
+ }
+
+ private int getNumber(int c) throws IOException {
+ int base = 10;
+ in.startString();
+ double dval = Double.NaN;
+ long longval = 0;
+ boolean isInteger = true;
+
+ // figure out what base we're using
+ if (c == '0') {
+ if (Character.toLowerCase((char)(c = in.read())) == 'x') { base = 16; in.startString(); }
+ else if (isDigit(c)) base = 8;
+ }
+
+ while (0 <= xDigitToInt(c) && !(base < 16 && isAlpha(c))) c = in.read();
+ if (base == 10 && (c == '.' || c == 'e' || c == 'E')) {
+ isInteger = false;
+ if (c == '.') do { c = in.read(); } while (isDigit(c));
+ if (c == 'e' || c == 'E') {
+ c = in.read();
+ if (c == '+' || c == '-') c = in.read();
+ if (!isDigit(c)) throw new LexerException("float listeral did not have an exponent value");
+ do { c = in.read(); } while (isDigit(c));
+ }
+ }
+ in.unread();
+
+ String numString = in.getString();
+ if (base == 10 && !isInteger) {
+ try { dval = (Double.valueOf(numString)).doubleValue(); }
+ catch (NumberFormatException ex) { throw new LexerException("invalid numeric literal: \"" + numString + "\""); }
+ } else {
+ if (isInteger) {
+ longval = Long.parseLong(numString, base);
+ dval = (double)longval;
+ } else {
+ dval = Double.parseDouble(numString);
+ longval = (long) dval;
+ if (longval == dval) isInteger = true;
+ }
+ }
+
+ if (!isInteger) this.number = JS.N(dval);
+ else this.number = JS.N(longval);
+ return NUMBER;
+ }
+
+ private int getString(int c) throws IOException {
+ StringBuffer stringBuf = null;
+ int quoteChar = c;
+ c = in.read();
+ in.startString(); // start after the first "
+ while(c != quoteChar) {
+ if (c == '\n' || c == -1) throw new LexerException("unterminated string literal");
+ if (c == '\\') {
+ if (stringBuf == null) {
+ in.unread(); // Don't include the backslash
+ stringBuf = new StringBuffer(in.getString());
+ in.read();
+ }
+ switch (c = in.read()) {
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'v': c = '\u000B'; break;
+ case '\\': c = '\\'; break;
+ case 'u': {
+ int v = 0;
+ for(int i=0; i<4; i++) {
+ int ci = in.read();
+ if (!((ci >= '0' && ci <= '9') || (ci >= 'a' && ci <= 'f') || (ci >= 'A' && ci <= 'F')))
+ throw new LexerException("illegal character '" + ((char)c) + "' in \\u unicode escape sequence");
+ v = (v << 8) | Integer.parseInt(ci + "", 16);
+ }
+ c = (char)v;
+ break;
+ }
+ default:
+ // just use the character that was escaped
+ break;
+ }
+ }
+ if (stringBuf != null) stringBuf.append((char) c);
+ c = in.read();
+ }
+ if (stringBuf != null) this.string = stringBuf.toString().intern();
+ else {
+ in.unread(); // miss the trailing "
+ this.string = in.getString().intern();
+ in.read();
+ }
+ return STRING;
+ }
+
+ private int _getToken() throws IOException {
+ int c;
+ do { c = in.read(); } while (c == '\u0020' || c == '\u0009' || c == '\u000C' || c == '\u000B' || c == '\n' );
+ if (c == -1) return -1;
+ if (c == '\\' || Character.isJavaIdentifierStart((char)c)) return getIdentifier(c);
+ if (isDigit(c) || (c == '.' && isDigit(in.peek()))) return getNumber(c);
+ if (c == '"' || c == '\'') return getString(c);
+ switch (c) {
+ case ';': return SEMI;
+ case '[': return LB;
+ case ']': return RB;
+ case '{': return LC;
+ case '}': return RC;
+ case '(': return LP;
+ case ')': return RP;
+ case ',': return COMMA;
+ case '?': return HOOK;
+ case ':': return !in.match(':') ? COLON : in.match('=') ? GRAMMAR : le(":: is not a valid token");
+ case '.': return DOT;
+ case '|': return in.match('|') ? OR : (in.match('=') ? ASSIGN_BITOR : BITOR);
+ case '^': return in.match('=') ? ASSIGN_BITXOR : BITXOR;
+ case '&': return in.match('&') ? AND : in.match('=') ? ASSIGN_BITAND : BITAND;
+ case '=': return !in.match('=') ? ASSIGN : in.match('=') ? SHEQ : EQ;
+ case '!': return !in.match('=') ? BANG : in.match('=') ? SHNE : NE;
+ case '%': return in.match('=') ? ASSIGN_MOD : MOD;
+ case '~': return BITNOT;
+ case '+': return in.match('=') ? ASSIGN_ADD : in.match('+') ? (in.match('=') ? ADD_TRAP : INC) : ADD;
+ case '-': return in.match('=') ? ASSIGN_SUB: in.match('-') ? (in.match('=') ? DEL_TRAP : DEC) : SUB;
+ case '*': return in.match('=') ? ASSIGN_MUL : MUL;
+ case '<': return !in.match('<') ? (in.match('=') ? LE : LT) : in.match('=') ? ASSIGN_LSH : LSH;
+ case '>': return !in.match('>') ? (in.match('=') ? GE : GT) :
+ in.match('>') ? (in.match('=') ? ASSIGN_URSH : URSH) : (in.match('=') ? ASSIGN_RSH : RSH);
+ case '/':
+ if (in.match('=')) return ASSIGN_DIV;
+ if (in.match('/')) { while ((c = in.read()) != -1 && c != '\n'); in.unread(); return getToken(); }
+ if (!in.match('*')) return DIV;
+ while ((c = in.read()) != -1 && !(c == '*' && in.match('/'))) {
+ if (c == '\n' || c != '/' || !in.match('*')) continue;
+ if (in.match('/')) return getToken();
+ throw new LexerException("nested comments are not permitted");
+ }
+ if (c == -1) throw new LexerException("unterminated comment");
+ return getToken(); // `goto retry'
+ default: throw new LexerException("illegal character: \'" + ((char)c) + "\'");
+ }
+ }
+
+ private int le(String s) throws LexerException { if (true) throw new LexerException(s); return 0; }
+
+ // SmartReader ////////////////////////////////////////////////////////////////
+
+ /** a Reader that tracks line numbers and can push back tokens */
+ private class SmartReader {
+ PushbackReader reader = null;
+ int lastread = -1;
+
+ public SmartReader(Reader r) { reader = new PushbackReader(r); }
+ public void unread() throws IOException { unread((char)lastread); }
+ public void unread(char c) throws IOException {
+ reader.unread(c);
+ if(c == '\n') col = -1;
+ else col--;
+ if (accumulator != null) accumulator.setLength(accumulator.length() - 1);
+ }
+ public boolean match(char c) throws IOException { if (peek() == c) { reader.read(); return true; } else return false; }
+ public int peek() throws IOException {
+ int peeked = reader.read();
+ if (peeked != -1) reader.unread((char)peeked);
+ return peeked;
+ }
+ public int read() throws IOException {
+ lastread = reader.read();
+ if (accumulator != null) accumulator.append((char)lastread);
+ if (lastread != '\n' && lastread != '\r') col++;
+ if (lastread == '\n') {
+ // col is -1 if we just unread a newline, this is sort of ugly
+ if (col != -1) parserLine = ++line;
+ col = 0;
+ }
+ return lastread;
+ }
+
+ // FEATURE: could be much more efficient
+ StringBuffer accumulator = null;
+ public void startString() {
+ accumulator = new StringBuffer();
+ accumulator.append((char)lastread);
+ }
+ public String getString() throws IOException {
+ String ret = accumulator.toString().intern();
+ accumulator = null;
+ return ret;
+ }
+ }
+
+
+ // Token PushBack code ////////////////////////////////////////////////////////////
+
+ private int pushBackDepth = 0;
+ private int[] pushBackInts = new int[10];
+ private Object[] pushBackObjects = new Object[10];
+
+ /** push back a token */
+ public final void pushBackToken(int op, Object obj) {
+ if (pushBackDepth >= pushBackInts.length - 1) {
+ int[] newInts = new int[pushBackInts.length * 2];
+ System.arraycopy(pushBackInts, 0, newInts, 0, pushBackInts.length);
+ pushBackInts = newInts;
+ Object[] newObjects = new Object[pushBackObjects.length * 2];
+ System.arraycopy(pushBackObjects, 0, newObjects, 0, pushBackObjects.length);
+ pushBackObjects = newObjects;
+ }
+ pushBackInts[pushBackDepth] = op;
+ pushBackObjects[pushBackDepth] = obj;
+ pushBackDepth++;
+ }
+
+ /** push back the most recently read token */
+ public final void pushBackToken() { pushBackToken(op, number != null ? (Object)number : (Object)string); }
+
+ /** read a token but leave it in the stream */
+ public final int peekToken() throws IOException {
+ int ret = getToken();
+ pushBackToken();
+ return ret;
+ }
+
+ /** read a token */
+ public final int getToken() throws IOException {
+ number = null;
+ string = null;
+ if (pushBackDepth == 0) {
+ mostRecentlyReadToken = op;
+ return op = _getToken();
+ }
+ pushBackDepth--;
+ op = pushBackInts[pushBackDepth];
+ if (pushBackObjects[pushBackDepth] != null) {
+ number = pushBackObjects[pushBackDepth] instanceof Number ? (Number)pushBackObjects[pushBackDepth] : null;
+ string = pushBackObjects[pushBackDepth] instanceof String ? (String)pushBackObjects[pushBackDepth] : null;
+ }
+ return op;
+ }
+
+ class LexerException extends IOException {
+ public LexerException(String s) { super(sourceName + ":" + line + "," + col + ": " + s); }
+ }
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/js/Parser.java b/upstream/org.ibex.core/build/java/org/ibex/js/Parser.java
new file mode 100644
index 0000000..51b23b8
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/js/Parser.java
@@ -0,0 +1,982 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+import org.ibex.util.*;
+import java.io.*;
+
+/**
+ * Parses a stream of lexed tokens into a tree of JSFunction's.
+ *
+ * There are three kinds of things we parse: blocks, statements, and
+ * expressions.
+ *
+ * - Expressions are a special type of statement that evaluates to a
+ * value (for example, "break" is not an expression, * but "3+2"
+ * is). Some tokens sequences start expressions (for * example,
+ * literal numbers) and others continue an expression which * has
+ * already been begun (for example, '+'). Finally, some *
+ * expressions are valid targets for an assignment operation; after
+ * * each of these expressions, continueExprAfterAssignable() is
+ * called * to check for an assignment operation.
+ *
+ * - A statement ends with a semicolon and does not return a value.
+ *
+ * - A block is a single statement or a sequence of statements
+ * surrounded by curly braces.
+ *
+ * Each parsing method saves the parserLine before doing its actual
+ * work and restores it afterwards. This ensures that parsing a
+ * subexpression does not modify the line number until a token
+ * *after* the subexpression has been consumed by the parent
+ * expression.
+ *
+ * Technically it would be a better design for this class to build an
+ * intermediate parse tree and use that to emit bytecode. Here's the
+ * tradeoff:
+ *
+ * Advantages of building a parse tree:
+ * - easier to apply optimizations
+ * - would let us handle more sophisticated languages than JavaScript
+ *
+ * Advantages of leaving out the parse tree
+ * - faster compilation
+ * - less load on the garbage collector
+ * - much simpler code, easier to understand
+ * - less error-prone
+ *
+ * Fortunately JS is such a simple language that we can get away with
+ * the half-assed approach and still produce a working, complete
+ * compiler.
+ *
+ * The bytecode language emitted doesn't really cause any appreciable
+ * semantic loss, and is itself a parseable language very similar to
+ * Forth or a postfix variant of LISP. This means that the bytecode
+ * can be transformed into a parse tree, which can be manipulated.
+ * So if we ever want to add an optimizer, it could easily be done by
+ * producing a parse tree from the bytecode, optimizing that tree,
+ * and then re-emitting the bytecode. The parse tree node class
+ * would also be much simpler since the bytecode language has so few
+ * operators.
+ *
+ * Actually, the above paragraph is slightly inaccurate -- there are
+ * places where we push a value and then perform an arbitrary number
+ * of operations using it before popping it; this doesn't parse well.
+ * But these cases are clearly marked and easy to change if we do
+ * need to move to a parse tree format.
+ */
+class Parser extends Lexer implements ByteCodes {
+
+
+ // Constructors //////////////////////////////////////////////////////
+
+ public Parser(Reader r, String sourceName, int line) throws IOException { super(r, sourceName, line); }
+
+ /** for debugging */
+ public static void main(String[] s) throws IOException {
+ JS block = JS.fromReader("stdin", 0, new InputStreamReader(System.in));
+ if (block == null) return;
+ System.out.println(block);
+ }
+
+
+ // Statics ////////////////////////////////////////////////////////////
+
+ static byte[] precedence = new byte[MAX_TOKEN + 1];
+ static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1];
+ // Use this as the precedence when we want anything up to the comma
+ private final static int NO_COMMA = 2;
+ static {
+ isRightAssociative[ASSIGN] =
+ isRightAssociative[ASSIGN_BITOR] =
+ isRightAssociative[ASSIGN_BITXOR] =
+ isRightAssociative[ASSIGN_BITAND] =
+ isRightAssociative[ASSIGN_LSH] =
+ isRightAssociative[ASSIGN_RSH] =
+ isRightAssociative[ASSIGN_URSH] =
+ isRightAssociative[ASSIGN_ADD] =
+ isRightAssociative[ASSIGN_SUB] =
+ isRightAssociative[ASSIGN_MUL] =
+ isRightAssociative[ASSIGN_DIV] =
+ isRightAssociative[ASSIGN_MOD] =
+ isRightAssociative[ADD_TRAP] =
+ isRightAssociative[DEL_TRAP] =
+ true;
+
+ precedence[COMMA] = 1;
+ // 2 is intentionally left unassigned. we use minPrecedence==2 for comma separated lists
+ precedence[ASSIGN] =
+ precedence[ASSIGN_BITOR] =
+ precedence[ASSIGN_BITXOR] =
+ precedence[ASSIGN_BITAND] =
+ precedence[ASSIGN_LSH] =
+ precedence[ASSIGN_RSH] =
+ precedence[ASSIGN_URSH] =
+ precedence[ASSIGN_ADD] =
+ precedence[ASSIGN_SUB] =
+ precedence[ASSIGN_MUL] =
+ precedence[ASSIGN_DIV] =
+ precedence[ADD_TRAP] =
+ precedence[DEL_TRAP] =
+ precedence[ASSIGN_MOD] = 3;
+ precedence[HOOK] = 4;
+ precedence[OR] = 5;
+ precedence[AND] = 6;
+ precedence[BITOR] = 7;
+ precedence[BITXOR] = 8;
+ precedence[BITAND] = 9;
+ precedence[EQ] = precedence[NE] = precedence[SHEQ] = precedence[SHNE] = 10;
+ precedence[LT] = precedence[LE] = precedence[GT] = precedence[GE] = 11;
+ precedence[LSH] = precedence[RSH] = precedence[URSH] = 12;
+ precedence[ADD] = precedence[SUB] = 12;
+ precedence[MUL] = precedence[DIV] = precedence[MOD] = 13;
+ precedence[BITNOT] = precedence[BANG] = precedence[TYPEOF] = 14;
+ precedence[DOT] = precedence[LB] = precedence[LP] = precedence[INC] = precedence[DEC] = 15;
+ }
+
+
+ // Parsing Logic /////////////////////////////////////////////////////////
+
+ /** gets a token and throws an exception if it is not code */
+ private void consume(int code) throws IOException {
+ if (getToken() != code) {
+ if(code == NAME) switch(op) {
+ case RETURN: case TYPEOF: case BREAK: case CONTINUE: case TRY: case THROW:
+ case ASSERT: case NULL: case TRUE: case FALSE: case IN: case IF: case ELSE:
+ case SWITCH: case CASE: case DEFAULT: case WHILE: case VAR: case WITH:
+ case CATCH: case FINALLY:
+ throw pe("Bad variable name; '" + codeToString[op].toLowerCase() + "' is a javascript keyword");
+ }
+ throw pe("expected " + codeToString[code] + ", got " + (op == -1 ? "EOF" : codeToString[op]));
+ }
+ }
+
+ /**
+ * Parse the largest possible expression containing no operators
+ * of precedence below minPrecedence and append the
+ * bytecodes for that expression to appendTo; the
+ * appended bytecodes MUST grow the stack by exactly one element.
+ */
+ private void startExpr(JSFunction appendTo, int minPrecedence) throws IOException {
+ int saveParserLine = parserLine;
+ _startExpr(appendTo, minPrecedence);
+ parserLine = saveParserLine;
+ }
+ private void _startExpr(JSFunction appendTo, int minPrecedence) throws IOException {
+ int tok = getToken();
+ JSFunction b = appendTo;
+
+ switch (tok) {
+ case -1: throw pe("expected expression");
+
+ // all of these simply push values onto the stack
+ case NUMBER: b.add(parserLine, LITERAL, number); break;
+ case STRING: b.add(parserLine, LITERAL, string); break;
+ case NULL: b.add(parserLine, LITERAL, null); break;
+ case TRUE: case FALSE: b.add(parserLine, LITERAL, JS.B(tok == TRUE)); break;
+
+ // (.foo) syntax
+ case DOT: {
+ consume(NAME);
+ b.add(parserLine, TOPSCOPE);
+ b.add(parserLine, LITERAL, "");
+ b.add(parserLine, GET);
+ b.add(parserLine, LITERAL, string);
+ b.add(parserLine, GET);
+ continueExpr(b, minPrecedence);
+ break;
+ }
+
+ case LB: {
+ b.add(parserLine, ARRAY, JS.ZERO); // push an array onto the stack
+ int size0 = b.size;
+ int i = 0;
+ if (peekToken() != RB)
+ while(true) { // iterate over the initialization values
+ b.add(parserLine, LITERAL, JS.N(i++)); // push the index in the array to place it into
+ if (peekToken() == COMMA || peekToken() == RB)
+ b.add(parserLine, LITERAL, null); // for stuff like [1,,2,]
+ else
+ startExpr(b, NO_COMMA); // push the value onto the stack
+ b.add(parserLine, PUT); // put it into the array
+ b.add(parserLine, POP); // discard the value remaining on the stack
+ if (peekToken() == RB) break;
+ consume(COMMA);
+ }
+ b.set(size0 - 1, JS.N(i)); // back at the ARRAY instruction, write the size of the array
+ consume(RB);
+ break;
+ }
+ case SUB: { // negative literal (like "3 * -1")
+ consume(NUMBER);
+ b.add(parserLine, LITERAL, JS.N(number.doubleValue() * -1));
+ break;
+ }
+ case LP: { // grouping (not calling)
+ startExpr(b, -1);
+ consume(RP);
+ break;
+ }
+ case INC: case DEC: { // prefix (not postfix)
+ startExpr(b, precedence[tok]);
+ int prev = b.size - 1;
+ if (b.get(prev) == GET && b.getArg(prev) != null)
+ b.set(prev, LITERAL, b.getArg(prev));
+ else if(b.get(prev) == GET)
+ b.pop();
+ else
+ throw pe("prefixed increment/decrement can only be performed on a valid assignment target");
+ b.add(parserLine, GET_PRESERVE, Boolean.TRUE);
+ b.add(parserLine, LITERAL, JS.N(1));
+ b.add(parserLine, tok == INC ? ADD : SUB, JS.N(2));
+ b.add(parserLine, PUT, null);
+ b.add(parserLine, SWAP, null);
+ b.add(parserLine, POP, null);
+ break;
+ }
+ case BANG: case BITNOT: case TYPEOF: {
+ startExpr(b, precedence[tok]);
+ b.add(parserLine, tok);
+ break;
+ }
+ case LC: { // object constructor
+ b.add(parserLine, OBJECT, null); // put an object on the stack
+ if (peekToken() != RC)
+ while(true) {
+ if (peekToken() != NAME && peekToken() != STRING)
+ throw pe("expected NAME or STRING");
+ getToken();
+ b.add(parserLine, LITERAL, string); // grab the key
+ consume(COLON);
+ startExpr(b, NO_COMMA); // grab the value
+ b.add(parserLine, PUT); // put the value into the object
+ b.add(parserLine, POP); // discard the remaining value
+ if (peekToken() == RC) break;
+ consume(COMMA);
+ if (peekToken() == RC) break; // we permit {,,} -- I'm not sure if ECMA does
+ }
+ consume(RC);
+ break;
+ }
+ case NAME: {
+ b.add(parserLine, TOPSCOPE);
+ b.add(parserLine, LITERAL, string);
+ continueExprAfterAssignable(b,minPrecedence);
+ break;
+ }
+ case FUNCTION: {
+ consume(LP);
+ int numArgs = 0;
+ JSFunction b2 = new JSFunction(sourceName, parserLine, null);
+ b.add(parserLine, NEWFUNCTION, b2);
+
+ // function prelude; arguments array is already on the stack
+ b2.add(parserLine, TOPSCOPE);
+ b2.add(parserLine, SWAP);
+ b2.add(parserLine, DECLARE, "arguments"); // declare arguments (equivalent to 'var arguments;')
+ b2.add(parserLine, SWAP); // set this.arguments and leave the value on the stack
+ b2.add(parserLine, PUT);
+
+ while(peekToken() != RP) { // run through the list of argument names
+ numArgs++;
+ if (peekToken() == NAME) {
+ consume(NAME); // a named argument
+ String varName = string;
+
+ b2.add(parserLine, DUP); // dup the args array
+ b2.add(parserLine, GET, JS.N(numArgs - 1)); // retrieve it from the arguments array
+ b2.add(parserLine, TOPSCOPE);
+ b2.add(parserLine, SWAP);
+ b2.add(parserLine, DECLARE, varName); // declare the name
+ b2.add(parserLine, SWAP);
+ b2.add(parserLine, PUT);
+ b2.add(parserLine, POP); // pop the value
+ b2.add(parserLine, POP); // pop the scope
+ }
+ if (peekToken() == RP) break;
+ consume(COMMA);
+ }
+ consume(RP);
+
+ b2.numFormalArgs = numArgs;
+ b2.add(parserLine, POP); // pop off the arguments array
+ b2.add(parserLine, POP); // pop off TOPSCOPE
+
+ if(peekToken() != LC)
+ throw pe("JSFunctions must have a block surrounded by curly brackets");
+
+ parseBlock(b2, null); // the function body
+
+ b2.add(parserLine, LITERAL, null); // in case we "fall out the bottom", return NULL
+ b2.add(parserLine, RETURN);
+
+ break;
+ }
+ default: throw pe("expected expression, found " + codeToString[tok] + ", which cannot start an expression");
+ }
+
+ // attempt to continue the expression
+ continueExpr(b, minPrecedence);
+ }
+ /*
+ private Grammar parseGrammar(Grammar g) throws IOException {
+ int tok = getToken();
+ if (g != null)
+ switch(tok) {
+ case BITOR: return new Grammar.Alternative(g, parseGrammar(null));
+ case ADD: return parseGrammar(new Grammar.Repetition(g, 1, Integer.MAX_VALUE));
+ case MUL: return parseGrammar(new Grammar.Repetition(g, 0, Integer.MAX_VALUE));
+ case HOOK: return parseGrammar(new Grammar.Repetition(g, 0, 1));
+ }
+ Grammar g0 = null;
+ switch(tok) {
+ //case NUMBER: g0 = new Grammar.Literal(number); break;
+ case NAME: g0 = new Grammar.Reference(string); break;
+ case STRING:
+ g0 = new Grammar.Literal(string);
+ if (peekToken() == DOT) {
+ String old = string;
+ consume(DOT);
+ consume(DOT);
+ consume(STRING);
+ if (old.length() != 1 || string.length() != 1) throw pe("literal ranges must be single-char strings");
+ g0 = new Grammar.Range(old.charAt(0), string.charAt(0));
+ }
+ break;
+ case LP: g0 = parseGrammar(null); consume(RP); break;
+ default: pushBackToken(); return g;
+ }
+ if (g == null) return parseGrammar(g0);
+ return parseGrammar(new Grammar.Juxtaposition(g, g0));
+ }
+ */
+ /**
+ * Assuming that a complete assignable (lvalue) has just been
+ * parsed and the object and key are on the stack,
+ * continueExprAfterAssignable will attempt to parse an
+ * expression that modifies the assignable. This method always
+ * decreases the stack depth by exactly one element.
+ */
+ private void continueExprAfterAssignable(JSFunction b,int minPrecedence) throws IOException {
+ int saveParserLine = parserLine;
+ _continueExprAfterAssignable(b,minPrecedence);
+ parserLine = saveParserLine;
+ }
+ private void _continueExprAfterAssignable(JSFunction b,int minPrecedence) throws IOException {
+ if (b == null) throw new Error("got null b; this should never happen");
+ int tok = getToken();
+ if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok])))
+ // force the default case
+ tok = -1;
+ switch(tok) {
+ /*
+ case GRAMMAR: {
+ b.add(parserLine, GET_PRESERVE);
+ Grammar g = parseGrammar(null);
+ if (peekToken() == LC) {
+ g.action = new JSFunction(sourceName, parserLine, null);
+ parseBlock((JSFunction)g.action);
+ ((JSFunction)g.action).add(parserLine, LITERAL, null); // in case we "fall out the bottom", return NULL
+ ((JSFunction)g.action).add(parserLine, RETURN);
+ }
+ b.add(parserLine, MAKE_GRAMMAR, g);
+ b.add(parserLine, PUT);
+ break;
+ }
+ */
+ case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH:
+ case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: case ASSIGN_ADD: case ASSIGN_SUB: case ADD_TRAP: case DEL_TRAP: {
+ if (tok != ADD_TRAP && tok != DEL_TRAP) b.add(parserLine, GET_PRESERVE);
+
+ startExpr(b, precedence[tok]);
+
+ if (tok != ADD_TRAP && tok != DEL_TRAP) {
+ // tok-1 is always s/^ASSIGN_// (0 is BITOR, 1 is ASSIGN_BITOR, etc)
+ b.add(parserLine, tok - 1, tok-1==ADD ? JS.N(2) : null);
+ b.add(parserLine, PUT);
+ b.add(parserLine, SWAP);
+ b.add(parserLine, POP);
+ } else {
+ b.add(parserLine, tok);
+ }
+ break;
+ }
+ case INC: case DEC: { // postfix
+ b.add(parserLine, GET_PRESERVE, Boolean.TRUE);
+ b.add(parserLine, LITERAL, JS.N(1));
+ b.add(parserLine, tok == INC ? ADD : SUB, JS.N(2));
+ b.add(parserLine, PUT, null);
+ b.add(parserLine, SWAP, null);
+ b.add(parserLine, POP, null);
+ b.add(parserLine, LITERAL, JS.N(1));
+ b.add(parserLine, tok == INC ? SUB : ADD, JS.N(2)); // undo what we just did, since this is postfix
+ break;
+ }
+ case ASSIGN: {
+ startExpr(b, precedence[tok]);
+ b.add(parserLine, PUT);
+ b.add(parserLine, SWAP);
+ b.add(parserLine, POP);
+ break;
+ }
+ case LP: {
+
+ // Method calls are implemented by doing a GET_PRESERVE
+ // first. If the object supports method calls, it will
+ // return JS.METHOD
+ int n = parseArgs(b, 2);
+ b.add(parserLine, GET_PRESERVE);
+ b.add(parserLine, CALLMETHOD, JS.N(n));
+ break;
+ }
+ default: {
+ pushBackToken();
+ if(b.get(b.size-1) == LITERAL && b.getArg(b.size-1) != null)
+ b.set(b.size-1,GET,b.getArg(b.size-1));
+ else
+ b.add(parserLine, GET);
+ return;
+ }
+ }
+ }
+
+
+ /**
+ * Assuming that a complete expression has just been parsed,
+ * continueExpr will attempt to extend this expression by
+ * parsing additional tokens and appending additional bytecodes.
+ *
+ * No operators with precedence less than minPrecedence
+ * will be parsed.
+ *
+ * If any bytecodes are appended, they will not alter the stack
+ * depth.
+ */
+ private void continueExpr(JSFunction b, int minPrecedence) throws IOException {
+ int saveParserLine = parserLine;
+ _continueExpr(b, minPrecedence);
+ parserLine = saveParserLine;
+ }
+ private void _continueExpr(JSFunction b, int minPrecedence) throws IOException {
+ if (b == null) throw new Error("got null b; this should never happen");
+ int tok = getToken();
+ if (tok == -1) return;
+ if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))) {
+ pushBackToken();
+ return;
+ }
+
+ switch (tok) {
+ case LP: { // invocation (not grouping)
+ int n = parseArgs(b, 1);
+ b.add(parserLine, CALL, JS.N(n));
+ break;
+ }
+ case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
+ case RSH: case URSH: case MUL: case DIV: case MOD:
+ case GT: case GE: case EQ: case NE: case LT: case LE: case SUB: {
+ startExpr(b, precedence[tok]);
+ b.add(parserLine, tok);
+ break;
+ }
+ case ADD: {
+ int count=1;
+ int nextTok;
+ do {
+ startExpr(b,precedence[tok]);
+ count++;
+ nextTok = getToken();
+ } while(nextTok == tok);
+ pushBackToken();
+ b.add(parserLine, tok, JS.N(count));
+ break;
+ }
+ case OR: case AND: {
+ b.add(parserLine, tok == AND ? JSFunction.JF : JSFunction.JT, JS.ZERO); // test to see if we can short-circuit
+ int size = b.size;
+ startExpr(b, precedence[tok]); // otherwise check the second value
+ b.add(parserLine, JMP, JS.N(2)); // leave the second value on the stack and jump to the end
+ b.add(parserLine, LITERAL, tok == AND ?
+ JS.B(false) : JS.B(true)); // target of the short-circuit jump is here
+ b.set(size - 1, JS.N(b.size - size)); // write the target of the short-circuit jump
+ break;
+ }
+ case DOT: {
+ // support foo..bar syntax for foo[""].bar
+ if (peekToken() == DOT) {
+ string = "";
+ } else {
+ consume(NAME);
+ }
+ b.add(parserLine, LITERAL, string);
+ continueExprAfterAssignable(b,minPrecedence);
+ break;
+ }
+ case LB: { // subscripting (not array constructor)
+ startExpr(b, -1);
+ consume(RB);
+ continueExprAfterAssignable(b,minPrecedence);
+ break;
+ }
+ case HOOK: {
+ b.add(parserLine, JF, JS.ZERO); // jump to the if-false expression
+ int size = b.size;
+ startExpr(b, minPrecedence); // write the if-true expression
+ b.add(parserLine, JMP, JS.ZERO); // if true, jump *over* the if-false expression
+ b.set(size - 1, JS.N(b.size - size + 1)); // now we know where the target of the jump is
+ consume(COLON);
+ size = b.size;
+ startExpr(b, minPrecedence); // write the if-false expression
+ b.set(size - 1, JS.N(b.size - size + 1)); // this is the end; jump to here
+ break;
+ }
+ case COMMA: {
+ // pop the result of the previous expression, it is ignored
+ b.add(parserLine,POP);
+ startExpr(b,-1);
+ break;
+ }
+ default: {
+ pushBackToken();
+ return;
+ }
+ }
+
+ continueExpr(b, minPrecedence); // try to continue the expression
+ }
+
+ // parse a set of comma separated function arguments, assume LP has already been consumed
+ // if swap is true, (because the function is already on the stack) we will SWAP after each argument to keep it on top
+ private int parseArgs(JSFunction b, int pushdown) throws IOException {
+ int i = 0;
+ while(peekToken() != RP) {
+ i++;
+ if (peekToken() != COMMA) {
+ startExpr(b, NO_COMMA);
+ b.add(parserLine, SWAP, JS.N(pushdown));
+ if (peekToken() == RP) break;
+ }
+ consume(COMMA);
+ }
+ consume(RP);
+ return i;
+ }
+
+ /** Parse a block of statements which must be surrounded by LC..RC. */
+ void parseBlock(JSFunction b) throws IOException { parseBlock(b, null); }
+ void parseBlock(JSFunction b, String label) throws IOException {
+ int saveParserLine = parserLine;
+ _parseBlock(b, label);
+ parserLine = saveParserLine;
+ }
+ void _parseBlock(JSFunction b, String label) throws IOException {
+ if (peekToken() == -1) return;
+ else if (peekToken() != LC) parseStatement(b, null);
+ else {
+ consume(LC);
+ while(peekToken() != RC && peekToken() != -1) parseStatement(b, null);
+ consume(RC);
+ }
+ }
+
+ /** Parse a single statement, consuming the RC or SEMI which terminates it. */
+ void parseStatement(JSFunction b, String label) throws IOException {
+ int saveParserLine = parserLine;
+ _parseStatement(b, label);
+ parserLine = saveParserLine;
+ }
+ void _parseStatement(JSFunction b, String label) throws IOException {
+ int tok = peekToken();
+ if (tok == -1) return;
+ switch(tok = getToken()) {
+
+ case THROW: case ASSERT: case RETURN: {
+ if (tok == RETURN && peekToken() == SEMI)
+ b.add(parserLine, LITERAL, null);
+ else
+ startExpr(b, -1);
+ b.add(parserLine, tok);
+ consume(SEMI);
+ break;
+ }
+ case BREAK: case CONTINUE: {
+ if (peekToken() == NAME) consume(NAME);
+ b.add(parserLine, tok, string);
+ consume(SEMI);
+ break;
+ }
+ case VAR: {
+ b.add(parserLine, TOPSCOPE); // push the current scope
+ while(true) {
+ consume(NAME);
+ b.add(parserLine, DECLARE, string); // declare it
+ if (peekToken() == ASSIGN) { // if there is an '=' after the variable name
+ consume(ASSIGN);
+ startExpr(b, NO_COMMA);
+ b.add(parserLine, PUT); // assign it
+ b.add(parserLine, POP); // clean the stack
+ } else {
+ b.add(parserLine, POP); // pop the string pushed by declare
+ }
+ if (peekToken() != COMMA) break;
+ consume(COMMA);
+ }
+ b.add(parserLine, POP); // pop off the topscope
+ if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
+ break;
+ }
+ case IF: {
+ consume(LP);
+ startExpr(b, -1);
+ consume(RP);
+
+ b.add(parserLine, JF, JS.ZERO); // if false, jump to the else-block
+ int size = b.size;
+ parseStatement(b, null);
+
+ if (peekToken() == ELSE) {
+ consume(ELSE);
+ b.add(parserLine, JMP, JS.ZERO); // if we took the true-block, jump over the else-block
+ b.set(size - 1, JS.N(b.size - size + 1));
+ size = b.size;
+ parseStatement(b, null);
+ }
+ b.set(size - 1, JS.N(b.size - size + 1)); // regardless of which branch we took, b[size] needs to point here
+ break;
+ }
+ case WHILE: {
+ consume(LP);
+ if (label != null) b.add(parserLine, LABEL, label);
+ b.add(parserLine, LOOP);
+ int size = b.size;
+ b.add(parserLine, POP); // discard the first-iteration indicator
+ startExpr(b, -1);
+ b.add(parserLine, JT, JS.N(2)); // if the while() clause is true, jump over the BREAK
+ b.add(parserLine, BREAK);
+ consume(RP);
+ parseStatement(b, null);
+ b.add(parserLine, CONTINUE); // if we fall out of the end, definately continue
+ b.set(size - 1, JS.N(b.size - size + 1)); // end of the loop
+ break;
+ }
+ case SWITCH: {
+ consume(LP);
+ if (label != null) b.add(parserLine, LABEL, label);
+ b.add(parserLine, LOOP);
+ int size0 = b.size;
+ startExpr(b, -1);
+ consume(RP);
+ consume(LC);
+ while(true)
+ if (peekToken() == CASE) { // we compile CASE statements like a bunch of if..else's
+ consume(CASE);
+ b.add(parserLine, DUP); // duplicate the switch() value; we'll consume one copy
+ startExpr(b, -1);
+ consume(COLON);
+ b.add(parserLine, EQ); // check if we should do this case-block
+ b.add(parserLine, JF, JS.ZERO); // if not, jump to the next one
+ int size = b.size;
+ while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) parseStatement(b, null);
+ b.set(size - 1, JS.N(1 + b.size - size));
+ } else if (peekToken() == DEFAULT) {
+ consume(DEFAULT);
+ consume(COLON);
+ while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) parseStatement(b, null);
+ } else if (peekToken() == RC) {
+ consume(RC);
+ b.add(parserLine, BREAK); // break out of the loop if we 'fall through'
+ break;
+ } else {
+ throw pe("expected CASE, DEFAULT, or RC; got " + codeToString[peekToken()]);
+ }
+ b.set(size0 - 1, JS.N(b.size - size0 + 1)); // end of the loop
+ break;
+ }
+
+ case DO: {
+ if (label != null) b.add(parserLine, LABEL, label);
+ b.add(parserLine, LOOP);
+ int size = b.size;
+ parseStatement(b, null);
+ consume(WHILE);
+ consume(LP);
+ startExpr(b, -1);
+ b.add(parserLine, JT, JS.N(2)); // check the while() clause; jump over the BREAK if true
+ b.add(parserLine, BREAK);
+ b.add(parserLine, CONTINUE);
+ consume(RP);
+ consume(SEMI);
+ b.set(size - 1, JS.N(b.size - size + 1)); // end of the loop; write this location to the LOOP instruction
+ break;
+ }
+
+ case TRY: {
+ b.add(parserLine, TRY); // try bytecode causes a TryMarker to be pushed
+ int tryInsn = b.size - 1;
+ // parse the expression to be TRYed
+ parseStatement(b, null);
+ // pop the try marker. this is pushed when the TRY bytecode is executed
+ b.add(parserLine, POP);
+ // jump forward to the end of the catch block, start of the finally block
+ b.add(parserLine, JMP);
+ int successJMPInsn = b.size - 1;
+
+ if (peekToken() != CATCH && peekToken() != FINALLY)
+ throw pe("try without catch or finally");
+
+ int catchJMPDistance = -1;
+ if (peekToken() == CATCH) {
+ Vec catchEnds = new Vec();
+ boolean catchAll = false;
+
+ catchJMPDistance = b.size - tryInsn;
+
+ while(peekToken() == CATCH && !catchAll) {
+ String exceptionVar;
+ getToken();
+ consume(LP);
+ consume(NAME);
+ exceptionVar = string;
+ int[] writebacks = new int[] { -1, -1, -1 };
+ if (peekToken() != RP) {
+ // extended Ibex catch block: catch(e faultCode "foo.bar.baz")
+ consume(NAME);
+ b.add(parserLine, DUP);
+ b.add(parserLine, LITERAL, string);
+ b.add(parserLine, GET);
+ b.add(parserLine, DUP);
+ b.add(parserLine, LITERAL, null);
+ b.add(parserLine, EQ);
+ b.add(parserLine, JT);
+ writebacks[0] = b.size - 1;
+ if (peekToken() == STRING) {
+ consume(STRING);
+ b.add(parserLine, DUP);
+ b.add(parserLine, LITERAL, string);
+ b.add(parserLine, LT);
+ b.add(parserLine, JT);
+ writebacks[1] = b.size - 1;
+ b.add(parserLine, DUP);
+ b.add(parserLine, LITERAL, string + "/"); // (slash is ASCII after dot)
+ b.add(parserLine, GE);
+ b.add(parserLine, JT);
+ writebacks[2] = b.size - 1;
+ } else {
+ consume(NUMBER);
+ b.add(parserLine, DUP);
+ b.add(parserLine, LITERAL, number);
+ b.add(parserLine, EQ);
+ b.add(parserLine, JF);
+ writebacks[1] = b.size - 1;
+ }
+ b.add(parserLine, POP); // pop the element thats on the stack from the compare
+ } else {
+ catchAll = true;
+ }
+ consume(RP);
+ // the exception is on top of the stack; put it to the chosen name
+ b.add(parserLine, NEWSCOPE);
+ b.add(parserLine, TOPSCOPE);
+ b.add(parserLine, SWAP);
+ b.add(parserLine, LITERAL,exceptionVar);
+ b.add(parserLine, DECLARE);
+ b.add(parserLine, SWAP);
+ b.add(parserLine, PUT);
+ b.add(parserLine, POP);
+ b.add(parserLine, POP);
+ parseBlock(b, null);
+ b.add(parserLine, OLDSCOPE);
+
+ b.add(parserLine, JMP);
+ catchEnds.addElement(new Integer(b.size-1));
+
+ for(int i=0; i<3; i++) if (writebacks[i] != -1) b.set(writebacks[i], JS.N(b.size-writebacks[i]));
+ b.add(parserLine, POP); // pop the element thats on the stack from the compare
+ }
+
+ if(!catchAll)
+ b.add(parserLine, THROW);
+
+ for(int i=0;i= 0 jump 5 down (to NEWSCOPE)
+ // Move the LoopMarker back into place - this is sort of ugly
+ b.add(parserLine, SWAP, JS.N(3));
+ b.add(parserLine, SWAP, JS.N(3));
+ b.add(parserLine, SWAP, JS.N(3));
+ // Stack is now: LoopMarker, -1, keys, obj, ...
+ b.add(parserLine, BREAK);
+
+ b.add(parserLine, NEWSCOPE);
+ if(hadVar) {
+ b.add(parserLine, DECLARE, varName);
+ b.add(parserLine, POP);
+ }
+
+ // Stack is now: index, keys, obj, LoopMarker, ...
+ b.add(parserLine, GET_PRESERVE); // key, index, keys, obj, LoopMarker, ...
+ b.add(parserLine, TOPSCOPE); // scope, key, index, keys, obj, LoopMarker, ...
+ b.add(parserLine, SWAP); // key, scope, index, keys, obj, LoopMarker, ...
+ b.add(parserLine, LITERAL, varName); // varName, key, scope, index, keys, obj, LoopMaker, ...
+ b.add(parserLine, SWAP); // key, varName, scope, index, keys, obj, LoopMarker, ...
+ b.add(parserLine, PUT); // key, scope, index, keys, obj, LoopMarker, ...
+ b.add(parserLine, POP); // scope, index, keys, obj, LoopMarker
+ b.add(parserLine, POP); // index, keys, obj, LoopMarker, ...
+ // Move the LoopMarker back into place - this is sort of ugly
+ b.add(parserLine, SWAP, JS.N(3));
+ b.add(parserLine, SWAP, JS.N(3));
+ b.add(parserLine, SWAP, JS.N(3));
+
+ parseStatement(b, null);
+
+ b.add(parserLine, OLDSCOPE);
+ b.add(parserLine, CONTINUE);
+ // jump here on break
+ b.set(size, JS.N(b.size - size));
+
+ b.add(parserLine, POP); // N
+ b.add(parserLine, POP); // KEYS
+ b.add(parserLine, POP); // OBJ
+
+ } else {
+ if (hadVar) pushBackToken(VAR, null); // yeah, this actually matters
+ b.add(parserLine, NEWSCOPE); // grab a fresh scope
+
+ parseStatement(b, null); // initializer
+ JSFunction e2 = // we need to put the incrementor before the test
+ new JSFunction(sourceName, parserLine, null); // so we save the test here
+ if (peekToken() != SEMI)
+ startExpr(e2, -1);
+ else
+ e2.add(parserLine, JSFunction.LITERAL, Boolean.TRUE); // handle the for(foo;;foo) case
+ consume(SEMI);
+ if (label != null) b.add(parserLine, LABEL, label);
+ b.add(parserLine, LOOP);
+ int size2 = b.size;
+
+ b.add(parserLine, JT, JS.ZERO); // if we're on the first iteration, jump over the incrementor
+ int size = b.size;
+ if (peekToken() != RP) { // do the increment thing
+ startExpr(b, -1);
+ b.add(parserLine, POP);
+ }
+ b.set(size - 1, JS.N(b.size - size + 1));
+ consume(RP);
+
+ b.paste(e2); // ok, *now* test if we're done yet
+ b.add(parserLine, JT, JS.N(2)); // break out if we don't meet the test
+ b.add(parserLine, BREAK);
+ parseStatement(b, null);
+ b.add(parserLine, CONTINUE); // if we fall out the bottom, CONTINUE
+ b.set(size2 - 1, JS.N(b.size - size2 + 1)); // end of the loop
+
+ b.add(parserLine, OLDSCOPE); // get our scope back
+ }
+ break;
+ }
+
+ case NAME: { // either a label or an identifier; this is the one place we're not LL(1)
+ String possiblyTheLabel = string;
+ if (peekToken() == COLON) { // label
+ consume(COLON);
+ parseStatement(b, possiblyTheLabel);
+ break;
+ } else { // expression
+ pushBackToken(NAME, possiblyTheLabel);
+ startExpr(b, -1);
+ b.add(parserLine, POP);
+ if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
+ break;
+ }
+ }
+
+ case SEMI: return; // yep, the null statement is valid
+
+ case LC: { // blocks are statements too
+ pushBackToken();
+ b.add(parserLine, NEWSCOPE);
+ parseBlock(b, label);
+ b.add(parserLine, OLDSCOPE);
+ break;
+ }
+
+ default: { // hope that it's an expression
+ pushBackToken();
+ startExpr(b, -1);
+ b.add(parserLine, POP);
+ if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
+ break;
+ }
+ }
+ }
+
+
+ // ParserException //////////////////////////////////////////////////////////////////////
+ private IOException pe(String s) { return new IOException(sourceName + ":" + line + " " + s); }
+
+}
+
diff --git a/upstream/org.ibex.core/build/java/org/ibex/js/Stream.java b/upstream/org.ibex.core/build/java/org/ibex/js/Stream.java
new file mode 100644
index 0000000..05045e9
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/js/Stream.java
@@ -0,0 +1,157 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+import java.io.*;
+import java.util.zip.*;
+import org.ibex.util.*;
+import org.ibex.plat.*;
+import org.ibex.net.*;
+
+/**
+ * Essentiall an InputStream "factory". You can repeatedly ask a
+ * Stream for an InputStream, and each InputStream you get back will
+ * be totally independent of the others (ie separate stream position
+ * and state) although they draw from the same data source.
+ */
+public abstract class Stream extends JS.Cloneable {
+
+ // Public Interface //////////////////////////////////////////////////////////////////////////////
+
+ public static InputStream getInputStream(Object js) throws IOException { return ((Stream)((JS)js).unclone()).getInputStream();}
+ public static class NotCacheableException extends Exception { }
+
+ // streams are "sealed" by default to prevent accidental object leakage
+ public void put(Object key, Object val) { }
+ private Cache getCache = new Cache(100);
+ protected Object _get(Object key) { return null; }
+ public final Object get(Object key) {
+ Object ret = getCache.get(key);
+ if (ret == null) getCache.put(key, ret = _get(key));
+ return ret;
+ }
+
+ // Private Interface //////////////////////////////////////////////////////////////////////////////
+
+ public abstract InputStream getInputStream() throws IOException;
+ protected String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); }
+
+ /** HTTP or HTTPS resource */
+ public static class HTTP extends Stream {
+ private String url;
+ public String toString() { return "Stream.HTTP:" + url; }
+ public HTTP(String url) { while (url.endsWith("/")) url = url.substring(0, url.length() - 1); this.url = url; }
+ public Object _get(Object key) { return new HTTP(url + "/" + (String)key); }
+ public String getCacheKey(Vec path) throws NotCacheableException { return url; }
+ public InputStream getInputStream() throws IOException { return new org.ibex.net.HTTP(url).GET(); }
+ }
+
+ /** byte arrays */
+ public static class ByteArray extends Stream {
+ private byte[] bytes;
+ private String cacheKey;
+ public ByteArray(byte[] bytes, String cacheKey) { this.bytes = bytes; this.cacheKey = cacheKey; }
+ public String getCacheKey() throws NotCacheableException {
+ if (cacheKey == null) throw new NotCacheableException(); return cacheKey; }
+ public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(bytes); }
+ }
+
+ /** a file */
+ public static class File extends Stream {
+ private String path;
+ public File(String path) { this.path = path; }
+ public String toString() { return "file:" + path; }
+ public String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); /* already on disk */ }
+ public InputStream getInputStream() throws IOException { return new FileInputStream(path); }
+ public Object _get(Object key) { return new File(path + java.io.File.separatorChar + (String)key); }
+ }
+
+ /** "unwrap" a Zip archive */
+ public static class Zip extends Stream {
+ private Stream parent;
+ private String path;
+ public Zip(Stream parent) { this(parent, null); }
+ public Zip(Stream parent, String path) {
+ while(path != null && path.startsWith("/")) path = path.substring(1);
+ this.parent = parent;
+ this.path = path;
+ }
+ public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!zip:"; }
+ public Object _get(Object key) { return new Zip(parent, path==null?(String)key:path+'/'+(String)key); }
+ public InputStream getInputStream() throws IOException {
+ InputStream pis = parent.getInputStream();
+ ZipInputStream zis = new ZipInputStream(pis);
+ ZipEntry ze = zis.getNextEntry();
+ while(ze != null && !ze.getName().equals(path)) ze = zis.getNextEntry();
+ if (ze == null) throw new IOException("requested file (" + path + ") not found in archive");
+ return new KnownLength.KnownLengthInputStream(zis, (int)ze.getSize());
+ }
+ }
+
+ /** "unwrap" a Cab archive */
+ public static class Cab extends Stream {
+ private Stream parent;
+ private String path;
+ public Cab(Stream parent) { this(parent, null); }
+ public Cab(Stream parent, String path) { this.parent = parent; this.path = path; }
+ public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!cab:"; }
+ public Object _get(Object key) { return new Cab(parent, path==null?(String)key:path+'/'+(String)key); }
+ public InputStream getInputStream() throws IOException { return new MSPack(parent.getInputStream()).getInputStream(path); }
+ }
+
+ /** the Builtin resource */
+ public static class Builtin extends Stream {
+ public String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); }
+ public InputStream getInputStream() throws IOException { return Platform.getBuiltinInputStream(); }
+ }
+
+ /** shadow resource which replaces the graft */
+ public static class ProgressWatcher extends Stream {
+ final Stream watchee;
+ JS callback;
+ public ProgressWatcher(Stream watchee, JS callback) { this.watchee = watchee; this.callback = callback; }
+ public String getCacheKey() throws NotCacheableException { return watchee.getCacheKey(); }
+ public InputStream getInputStream() throws IOException {
+ final InputStream is = watchee.getInputStream();
+ return new FilterInputStream(is) {
+ int bytesDownloaded = 0;
+ public int read() throws IOException {
+ int ret = super.read();
+ if (ret != -1) bytesDownloaded++;
+ return ret;
+ }
+ public int read(byte[] b, int off, int len) throws IOException {
+ int ret = super.read(b, off, len);
+ if (ret != 1) bytesDownloaded += ret;
+ Scheduler.add(new Task() { public void perform() throws IOException, JSExn {
+ callback.call(N(bytesDownloaded),
+ N(is instanceof KnownLength ? ((KnownLength)is).getLength() : 0), null, null, 2);
+ } });
+ return ret;
+ }
+ };
+ }
+ }
+
+ /** subclass from this if you want a CachedInputStream for each path */
+ public static class CachedStream extends Stream {
+ private Stream parent;
+ private boolean disk = false;
+ private String key;
+ public String getCacheKey() throws NotCacheableException { return key; }
+ CachedInputStream cis = null;
+ public CachedStream(Stream p, String s, boolean d) throws NotCacheableException {
+ this.parent = p; this.disk = d; this.key = p.getCacheKey();
+ }
+ public InputStream getInputStream() throws IOException {
+ if (cis != null) return cis.getInputStream();
+ if (!disk) {
+ cis = new CachedInputStream(parent.getInputStream());
+ } else {
+ java.io.File f = org.ibex.core.LocalStorage.Cache.getCacheFileForKey(key);
+ if (f.exists()) return new FileInputStream(f);
+ cis = new CachedInputStream(parent.getInputStream(), f);
+ }
+ return cis.getInputStream();
+ }
+ }
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/js/Tokens.java b/upstream/org.ibex.core/build/java/org/ibex/js/Tokens.java
new file mode 100644
index 0000000..8970e14
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/js/Tokens.java
@@ -0,0 +1,120 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+/** this class contains a public static final int for each valid token */
+interface Tokens {
+
+ // Token Constants //////////////////////////////////////////////////////////
+
+ // arithmetic operations; also valid as bytecodes
+ public static final int BITOR = 0; // |
+ public static final int ASSIGN_BITOR = 1; // |=
+ public static final int BITXOR = 2; // ^
+ public static final int ASSIGN_BITXOR = 3; // ^=
+ public static final int BITAND = 4; // &
+ public static final int ASSIGN_BITAND = 5; // &=
+ public static final int LSH = 6; // <<
+ public static final int ASSIGN_LSH = 7; // <<=
+ public static final int RSH = 8; // >>
+ public static final int ASSIGN_RSH = 9; // >>=
+ public static final int URSH = 10; // >>>
+ public static final int ASSIGN_URSH = 11; // >>>=
+ public static final int ADD = 12; // +
+ public static final int ASSIGN_ADD = 13; // +=
+ public static final int SUB = 14; // -
+ public static final int ASSIGN_SUB = 15; // -=
+ public static final int MUL = 16; // *
+ public static final int ASSIGN_MUL = 17; // *=
+ public static final int DIV = 18; // /
+ public static final int ASSIGN_DIV = 19; // /=
+ public static final int MOD = 20; // %
+ public static final int ASSIGN_MOD = 21; // %=
+ public static final int BITNOT = 22; // ~
+ public static final int ASSIGN_BITNOT = 23; // ~=
+
+ // logical operations; also valid as bytecodes
+ public static final int OR = 24; // ||
+ public static final int AND = 25; // &&
+ public static final int BANG = 26; // !
+
+ // equality operations; also valid as bytecodes
+ public static final int EQ = 27; // ==
+ public static final int NE = 28; // !=
+ public static final int LT = 29; // <
+ public static final int LE = 30; // <=
+ public static final int GT = 31; // >
+ public static final int GE = 32; // >=
+ public static final int SHEQ = 33; // ===
+ public static final int SHNE = 34; // !==
+
+ // other permissible bytecode tokens
+ public static final int RETURN = 35; // return
+ public static final int TYPEOF = 36; // typeof
+ public static final int BREAK = 37; // break keyword
+ public static final int CONTINUE = 38; // continue keyword
+ public static final int TRY = 39; // try
+ public static final int THROW = 40; // throw
+ public static final int ASSERT = 41; // assert keyword
+
+ public static final int NAME = 42; // *** identifiers ***
+ public static final int NUMBER = 43; // *** numeric literals ***
+ public static final int STRING = 44; // *** string literals ***
+ public static final int NULL = 45; // null
+ public static final int THIS = 46; // this
+ public static final int FALSE = 47; // false
+ public static final int TRUE = 48; // true
+ public static final int IN = 49; // in
+
+ public static final int SEMI = 50; // ;
+ public static final int LB = 51; // [
+ public static final int RB = 52; // ]
+ public static final int LC = 53; // {
+ public static final int RC = 54; // }
+ public static final int LP = 55; // (
+ public static final int RP = 56; // )
+ public static final int COMMA = 57; // ,
+ public static final int ASSIGN = 58; // =
+ public static final int HOOK = 59; // ?
+ public static final int COLON = 60; // :
+ public static final int INC = 61; // ++
+ public static final int DEC = 62; // --
+ public static final int DOT = 63; // .
+ public static final int FUNCTION = 64; // function
+ public static final int IF = 65; // if keyword
+ public static final int ELSE = 66; // else keyword
+ public static final int SWITCH = 67; // switch keyword
+ public static final int CASE = 68; // case keyword
+ public static final int DEFAULT = 69; // default keyword
+ public static final int WHILE = 70; // while keyword
+ public static final int DO = 71; // do keyword
+ public static final int FOR = 72; // for keyword
+ public static final int VAR = 73; // var keyword
+ public static final int WITH = 74; // with keyword
+ public static final int CATCH = 75; // catch keyword
+ public static final int FINALLY = 76; // finally keyword
+ public static final int RESERVED = 77; // reserved keyword
+ public static final int GRAMMAR = 78; // the grammar-definition operator (::=)
+ public static final int ADD_TRAP = 79; // the add-trap operator (++=)
+ public static final int DEL_TRAP = 80; // the del-trap operator (--=)
+
+ public static final int MAX_TOKEN = DEL_TRAP;
+
+ public final static String[] codeToString = new String[] {
+ "BITOR", "ASSIGN_BITOR", "BITXOR", "ASSIGN_BITXOR", "BITAND",
+ "ASSIGN_BITAND", "LSH", "ASSIGN_LSH", "RSH", "ASSIGN_RSH",
+ "URSH", "ASSIGN_URSH", "ADD", "ASSIGN_ADD", "SUB",
+ "ASSIGN_SUB", "MUL", "ASSIGN_MUL", "DIV", "ASSIGN_DIV", "MOD",
+ "ASSIGN_MOD", "BITNOT", "ASSIGN_BITNOT", "OR", "AND", "BANG",
+ "EQ", "NE", "LT", "LE", "GT", "GE", "SHEQ", "SHNE", "RETURN",
+ "TYPEOF", "BREAK", "CONTINUE", "TRY", "THROW", "ASSERT", "NAME",
+ "NUMBER", "STRING", "NULL", "THIS", "FALSE", "TRUE", "IN",
+ "SEMI", "LB", "RB", "LC", "RC", "LP", "RP", "COMMA", "ASSIGN",
+ "HOOK", "COLON", "INC", "DEC", "DOT", "FUNCTION", "IF",
+ "ELSE", "SWITCH", "CASE", "DEFAULT", "WHILE", "DO", "FOR",
+ "VAR", "WITH", "CATCH", "FINALLY", "RESERVED", "GRAMMAR",
+ "ADD_TRAP", "DEL_TRAP"
+ };
+
+}
+
+
diff --git a/upstream/org.ibex.core/build/java/org/ibex/js/Trap.java b/upstream/org.ibex.core/build/java/org/ibex/js/Trap.java
new file mode 100644
index 0000000..77476f6
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/js/Trap.java
@@ -0,0 +1,61 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+/**
+ * This class encapsulates a single trap placed on a given node. The
+ * traps for a given property name on a given box are maintained as a
+ * linked list stack, with the most recently placed trap at the head
+ * of the list.
+ */
+class Trap {
+
+ JS trapee = null; ///< the box on which this trap was placed
+ Object name = null; ///< the property that the trap was placed on
+
+ JSFunction f = null; ///< the function for this trap
+ Trap next = null; ///< the next trap down the trap stack
+
+ Trap(JS b, String n, JSFunction f, Trap nx) {
+ trapee = b; name = n; this.f = f; this.next = nx;
+ }
+
+ static final JSFunction putInvoker = new JSFunction("putInvoker", 0, null);
+ static final JSFunction getInvoker = new JSFunction("getInvoker", 0, null);
+
+ static {
+ putInvoker.add(1, ByteCodes.PUT, null);
+ putInvoker.add(2, Tokens.RETURN, null);
+ getInvoker.add(1, ByteCodes.GET, null);
+ getInvoker.add(2, Tokens.RETURN, null);
+ }
+
+ void invoke(Object value) throws JSExn {
+ Interpreter i = new Interpreter(putInvoker, false, null);
+ i.stack.push(trapee);
+ i.stack.push(name);
+ i.stack.push(value);
+ i.resume();
+ }
+
+ Object invoke() throws JSExn {
+ Interpreter i = new Interpreter(getInvoker, false, null);
+ i.stack.push(trapee);
+ i.stack.push(name);
+ return i.resume();
+ }
+
+ // FIXME: review; is necessary?
+ static class TrapScope extends JSScope {
+ Trap t;
+ Object val = null;
+ boolean cascadeHappened = false;
+ public TrapScope(JSScope parent, Trap t, Object val) { super(parent); this.t = t; this.val = val; }
+ public Object get(Object key) throws JSExn {
+ if (key.equals("trapee")) return t.trapee;
+ if (key.equals("callee")) return t.f;
+ if (key.equals("trapname")) return t.name;
+ return super.get(key);
+ }
+ }
+}
+
diff --git a/upstream/org.ibex.core/build/java/org/ibex/net/HTTP.java b/upstream/org.ibex.core/build/java/org/ibex/net/HTTP.java
new file mode 100644
index 0000000..5c7373e
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/net/HTTP.java
@@ -0,0 +1,1303 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.net;
+
+import java.net.*;
+import java.io.*;
+import java.util.*;
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.plat.*;
+import org.ibex.core.*;
+import org.ibex.crypto.*;
+
+/**
+ * This object encapsulates a *single* HTTP connection. Multiple requests may be pipelined over a connection (thread-safe),
+ * although any IOException encountered in a request will invalidate all later requests.
+ */
+public class HTTP {
+
+
+ // Public Methods ////////////////////////////////////////////////////////////////////////////////////////
+
+ public HTTP(String url) { this(url, false); }
+ public HTTP(String url, boolean skipResolveCheck) { originalUrl = url; this.skipResolveCheck = skipResolveCheck; }
+
+ /** Performs an HTTP GET request */
+ public InputStream GET() throws IOException { return makeRequest(null, null); }
+
+ /** Performs an HTTP POST request; content is additional headers, blank line, and body */
+ public InputStream POST(String contentType, String content) throws IOException { return makeRequest(contentType, content); }
+
+ public static class HTTPException extends IOException { public HTTPException(String s) { super(s); } }
+
+ public static HTTP stdio = new HTTP("stdio:");
+
+
+ // Statics ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ static Hash resolvedHosts = new Hash(); ///< cache for resolveAndCheckIfFirewalled()
+ private static Hash authCache = new Hash(); ///< cache of userInfo strings, keyed on originalUrl
+
+
+ // Instance Data ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ final String originalUrl; ///< the URL as passed to the original constructor; this is never changed
+ String url = null; ///< the URL to connect to; this is munged when the url is parsed */
+ String host = null; ///< the host to connect to
+ int port = -1; ///< the port to connect on
+ boolean ssl = false; ///< true if SSL (HTTPS) should be used
+ String path = null; ///< the path (URI) to retrieve on the server
+ Socket sock = null; ///< the socket
+ InputStream in = null; ///< the socket's inputstream
+ String userInfo = null; ///< the username and password portions of the URL
+ boolean firstRequest = true; ///< true iff this is the first request to be made on this socket
+ boolean skipResolveCheck = false; ///< allowed to skip the resolve check when downloading PAC script
+ boolean proxied = false; ///< true iff we're using a proxy
+
+ /** this is null if the current request is the first request on
+ * this HTTP connection; otherwise it is a Semaphore which will be
+ * released once the request ahead of us has recieved its response
+ */
+ Semaphore okToRecieve = null;
+
+ /**
+ * This method isn't synchronized; however, only one thread can be in the inner synchronized block at a time, and the rest of
+ * the method is protected by in-order one-at-a-time semaphore lock-steps
+ */
+ private InputStream makeRequest(String contentType, String content) throws IOException {
+
+ // Step 1: send the request and establish a semaphore to stop any requests that pipeline after us
+ Semaphore blockOn = null;
+ Semaphore releaseMe = null;
+ synchronized(this) {
+ try {
+ connect();
+ sendRequest(contentType, content);
+ } catch (IOException e) {
+ reset();
+ throw e;
+ }
+ blockOn = okToRecieve;
+ releaseMe = okToRecieve = new Semaphore();
+ }
+
+ // Step 2: wait for requests ahead of us to complete, then read the reply off the stream
+ boolean doRelease = true;
+ try {
+ if (blockOn != null) blockOn.block();
+
+ // previous call wrecked the socket connection, but we already sent our request, so we can't just retry --
+ // this could cause the server to receive the request twice, which could be bad (think of the case where the
+ // server call causes Amazon.com to ship you an item with one-click purchasing).
+ if (in == null)
+ throw new HTTPException("a previous pipelined call messed up the socket");
+
+ Hashtable h = in == null ? null : parseHeaders(in);
+ if (h == null) {
+ if (firstRequest) throw new HTTPException("server closed the socket with no response");
+ // sometimes the server chooses to close the stream between requests
+ reset();
+ releaseMe.release();
+ return makeRequest(contentType, content);
+ }
+
+ String reply = h.get("STATUSLINE").toString();
+
+ if (reply.startsWith("407") || reply.startsWith("401")) {
+
+ if (reply.startsWith("407")) doProxyAuth(h, content == null ? "GET" : "POST");
+ else doWebAuth(h, content == null ? "GET" : "POST");
+
+ if (h.get("HTTP").equals("1.0") && h.get("content-length") == null) {
+ if (Log.on) Log.info(this, "proxy returned an HTTP/1.0 reply with no content-length...");
+ reset();
+ } else {
+ int cl = h.get("content-length") == null ? -1 : Integer.parseInt(h.get("content-length").toString());
+ new HTTPInputStream(in, cl, releaseMe).close();
+ }
+ releaseMe.release();
+ return makeRequest(contentType, content);
+
+ } else if (reply.startsWith("2")) {
+ if (h.get("HTTP").equals("1.0") && h.get("content-length") == null)
+ throw new HTTPException("Ibex does not support HTTP/1.0 servers which fail to return the Content-Length header");
+ int cl = h.get("content-length") == null ? -1 : Integer.parseInt(h.get("content-length").toString());
+ InputStream ret = new HTTPInputStream(in, cl, releaseMe);
+ if ("gzip".equals(h.get("content-encoding"))) ret = new java.util.zip.GZIPInputStream(ret);
+ doRelease = false;
+ return ret;
+
+ } else {
+ throw new HTTPException("HTTP Error: " + reply);
+
+ }
+
+ } catch (IOException e) { reset(); throw e;
+ } finally { if (doRelease) releaseMe.release();
+ }
+ }
+
+
+ // Safeguarded DNS Resolver ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ * resolves the hostname and returns it as a string in the form "x.y.z.w"
+ * @throws HTTPException if the host falls within a firewalled netblock
+ */
+ private void resolveAndCheckIfFirewalled(String host) throws HTTPException {
+
+ // cached
+ if (resolvedHosts.get(host) != null) return;
+
+ // if all scripts are trustworthy (local FS), continue
+ if (Main.originAddr == null) return;
+
+ // resolve using DNS
+ try {
+ InetAddress addr = InetAddress.getByName(host);
+ byte[] quadbyte = addr.getAddress();
+ if ((quadbyte[0] == 10 ||
+ (quadbyte[0] == 192 && quadbyte[1] == 168) ||
+ (quadbyte[0] == 172 && (quadbyte[1] & 0xF0) == 16)) && !addr.equals(Main.originAddr))
+ throw new HTTPException("security violation: " + host + " [" + addr.getHostAddress() +
+ "] is in a firewalled netblock");
+ return;
+ } catch (UnknownHostException uhe) { }
+
+ if (Platform.detectProxy() == null)
+ throw new HTTPException("could not resolve hostname \"" + host + "\" and no proxy configured");
+ }
+
+
+ // Methods to attempt socket creation /////////////////////////////////////////////////////////////////
+
+ private Socket getSocket(String host, int port, boolean ssl, boolean negotiate) throws IOException {
+ Socket ret = ssl ? new SSL(host, port, negotiate) : new Socket(java.net.InetAddress.getByName(host), port);
+ ret.setTcpNoDelay(true);
+ return ret;
+ }
+
+ /** Attempts a direct connection */
+ private Socket attemptDirect() {
+ try {
+ Log.info(this, "attempting to create unproxied socket to " +
+ host + ":" + port + (ssl ? " [ssl]" : ""));
+ return getSocket(host, port, ssl, true);
+ } catch (IOException e) {
+ if (Log.on) Log.info(this, "exception in attemptDirect(): " + e);
+ return null;
+ }
+ }
+
+ /** Attempts to use an HTTP proxy, employing the CONNECT method if HTTPS is requested */
+ private Socket attemptHttpProxy(String proxyHost, int proxyPort) {
+ try {
+ if (Log.verbose) Log.info(this, "attempting to create HTTP proxied socket using proxy " + proxyHost + ":" + proxyPort);
+ Socket sock = getSocket(proxyHost, proxyPort, ssl, false);
+
+ if (!ssl) {
+ if (!path.startsWith("http://")) path = "http://" + host + ":" + port + path;
+ return sock;
+ }
+
+ PrintWriter pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream()));
+ BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream()));
+ pw.print("CONNECT " + host + ":" + port + " HTTP/1.1\r\n\r\n");
+ pw.flush();
+ String s = br.readLine();
+ if (s.charAt(9) != '2') throw new HTTPException("proxy refused CONNECT method: \"" + s + "\"");
+ while (br.readLine().length() > 0) { };
+ ((SSL)sock).negotiate();
+ return sock;
+
+ } catch (IOException e) {
+ if (Log.on) Log.info(this, "exception in attemptHttpProxy(): " + e);
+ return null;
+ }
+ }
+
+ /**
+ * Implements SOCKSv4 with v4a DNS extension
+ * @see http://www.socks.nec.com/protocol/socks4.protocol
+ * @see http://www.socks.nec.com/protocol/socks4a.protocol
+ */
+ private Socket attemptSocksProxy(String proxyHost, int proxyPort) {
+
+ // even if host is already a "x.y.z.w" string, we use this to parse it into bytes
+ InetAddress addr = null;
+ try { addr = InetAddress.getByName(host); } catch (Exception e) { }
+
+ if (Log.verbose) Log.info(this, "attempting to create SOCKSv4" + (addr == null ? "" : "a") +
+ " proxied socket using proxy " + proxyHost + ":" + proxyPort);
+
+ try {
+ Socket sock = getSocket(proxyHost, proxyPort, ssl, false);
+
+ DataOutputStream dos = new DataOutputStream(sock.getOutputStream());
+ dos.writeByte(0x04); // SOCKSv4(a)
+ dos.writeByte(0x01); // CONNECT
+ dos.writeShort(port & 0xffff); // port
+ if (addr == null) dos.writeInt(0x00000001); // bogus IP
+ else dos.write(addr.getAddress()); // actual IP
+ dos.writeByte(0x00); // no userid
+ if (addr == null) {
+ PrintWriter pw = new PrintWriter(new OutputStreamWriter(dos));
+ pw.print(host);
+ pw.flush();
+ dos.writeByte(0x00); // hostname null terminator
+ }
+ dos.flush();
+
+ DataInputStream dis = new DataInputStream(sock.getInputStream());
+ dis.readByte(); // reply version
+ byte success = dis.readByte(); // success/fail
+ dis.skip(6); // ip/port
+
+ if ((int)(success & 0xff) == 90) {
+ if (ssl) ((SSL)sock).negotiate();
+ return sock;
+ }
+ if (Log.on) Log.info(this, "SOCKS server denied access, code " + (success & 0xff));
+ return null;
+
+ } catch (IOException e) {
+ if (Log.on) Log.info(this, "exception in attemptSocksProxy(): " + e);
+ return null;
+ }
+ }
+
+ /** executes the PAC script and dispatches a call to one of the other attempt methods based on the result */
+ private Socket attemptPAC(org.ibex.js.JS pacFunc) {
+ if (Log.verbose) Log.info(this, "evaluating PAC script");
+ String pac = null;
+ try {
+ Object obj = pacFunc.call(url, host, null, null, 2);
+ if (Log.verbose) Log.info(this, " PAC script returned \"" + obj + "\"");
+ pac = obj.toString();
+ } catch (Throwable e) {
+ if (Log.on) Log.info(this, "PAC script threw exception " + e);
+ return null;
+ }
+
+ StringTokenizer st = new StringTokenizer(pac, ";", false);
+ while (st.hasMoreTokens()) {
+ String token = st.nextToken().trim();
+ if (Log.verbose) Log.info(this, " trying \"" + token + "\"...");
+ try {
+ Socket ret = null;
+ if (token.startsWith("DIRECT"))
+ ret = attemptDirect();
+ else if (token.startsWith("PROXY"))
+ ret = attemptHttpProxy(token.substring(token.indexOf(' ') + 1, token.indexOf(':')),
+ Integer.parseInt(token.substring(token.indexOf(':') + 1)));
+ else if (token.startsWith("SOCKS"))
+ ret = attemptSocksProxy(token.substring(token.indexOf(' ') + 1, token.indexOf(':')),
+ Integer.parseInt(token.substring(token.indexOf(':') + 1)));
+ if (ret != null) return ret;
+ } catch (Throwable e) {
+ if (Log.on) Log.info(this, "attempt at \"" + token + "\" failed due to " + e + "; trying next token");
+ }
+ }
+ if (Log.on) Log.info(this, "all PAC results exhausted");
+ return null;
+ }
+
+
+ // Everything Else ////////////////////////////////////////////////////////////////////////////
+
+ private synchronized void connect() throws IOException {
+ if (originalUrl.equals("stdio:")) { in = new BufferedInputStream(System.in); return; }
+ if (sock != null) {
+ if (in == null) in = new BufferedInputStream(sock.getInputStream());
+ return;
+ }
+ // grab the userinfo; gcj doesn't have java.net.URL.getUserInfo()
+ String url = originalUrl;
+ userInfo = url.substring(url.indexOf("://") + 3);
+ userInfo = userInfo.indexOf('/') == -1 ? userInfo : userInfo.substring(0, userInfo.indexOf('/'));
+ if (userInfo.indexOf('@') != -1) {
+ userInfo = userInfo.substring(0, userInfo.indexOf('@'));
+ url = url.substring(0, url.indexOf("://") + 3) + url.substring(url.indexOf('@') + 1);
+ } else {
+ userInfo = null;
+ }
+
+ if (url.startsWith("https:")) {
+ ssl = true;
+ } else if (!url.startsWith("http:")) {
+ throw new IOException("HTTP only supports http/https urls");
+ }
+ if (url.indexOf("://") == -1) throw new IOException("URLs must contain a ://");
+ String temphost = url.substring(url.indexOf("://") + 3);
+ path = temphost.substring(temphost.indexOf('/'));
+ temphost = temphost.substring(0, temphost.indexOf('/'));
+ if (temphost.indexOf(':') != -1) {
+ port = Integer.parseInt(temphost.substring(temphost.indexOf(':')+1));
+ temphost = temphost.substring(0, temphost.indexOf(':'));
+ } else {
+ port = ssl ? 443 : 80;
+ }
+ if (!skipResolveCheck) resolveAndCheckIfFirewalled(temphost);
+ host = temphost;
+ if (Log.verbose) Log.info(this, "creating HTTP object for connection to " + host + ":" + port);
+
+ Proxy pi = Platform.detectProxy();
+ OUTER: do {
+ if (pi != null) {
+ for(int i=0; i length) len = length;
+ int ret = b == null ? (int)super.skip(len) : super.read(b, off, len);
+ if (ret >= 0) {
+ length -= ret;
+ good = true;
+ }
+ return ret;
+ } finally {
+ if (!good) reset();
+ }
+ }
+
+ public void close() throws IOException {
+ if (contentLength == -1) {
+ while(!chunkedDone) {
+ if (length != 0) skip(length);
+ readChunk();
+ }
+ skip(2);
+ } else {
+ if (length != 0) skip(length);
+ }
+ if (releaseMe != null) releaseMe.release();
+ }
+ }
+
+ void reset() {
+ firstRequest = true;
+ in = null;
+ sock = null;
+ }
+
+
+ // Misc Helpers ///////////////////////////////////////////////////////////////////////////////////
+
+ /** reads a set of HTTP headers off of the input stream, returning null if the stream is already at its end */
+ private Hashtable parseHeaders(InputStream in) throws IOException {
+ Hashtable ret = new Hashtable();
+
+ // we can't use a BufferedReader directly on the input stream, since it will buffer past the end of the headers
+ byte[] buf = new byte[4096];
+ int buflen = 0;
+ while(true) {
+ int read = in.read();
+ if (read == -1 && buflen == 0) return null;
+ if (read == -1) throw new HTTPException("stream closed while reading headers");
+ buf[buflen++] = (byte)read;
+ if (buflen >= 4 && buf[buflen - 4] == '\r' && buf[buflen - 3] == '\n' &&
+ buf[buflen - 2] == '\r' && buf[buflen - 1] == '\n')
+ break;
+ if (buflen >=2 && buf[buflen - 1] == '\n' && buf[buflen - 2] == '\n')
+ break; // nice for people using stdio
+ if (buflen == buf.length) {
+ byte[] newbuf = new byte[buf.length * 2];
+ System.arraycopy(buf, 0, newbuf, 0, buflen);
+ buf = newbuf;
+ }
+ }
+
+ BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, buflen)));
+ String s = br.readLine();
+ if (!s.startsWith("HTTP/")) throw new HTTPException("Expected reply to start with \"HTTP/\", got: " + s);
+ ret.put("STATUSLINE", s.substring(s.indexOf(' ') + 1));
+ ret.put("HTTP", s.substring(5, s.indexOf(' ')));
+
+ while((s = br.readLine()) != null && s.length() > 0) {
+ String front = s.substring(0, s.indexOf(':')).toLowerCase();
+ String back = s.substring(s.indexOf(':') + 1).trim();
+ // ugly hack: we never replace a Digest-auth with a Basic-auth (proxy + www)
+ if (front.endsWith("-authenticate") && ret.get(front) != null && !back.equals("Digest")) continue;
+ ret.put(front, back);
+ }
+ return ret;
+ }
+
+ private Hashtable parseAuthenticationChallenge(String s) {
+ Hashtable ret = new Hashtable();
+
+ s = s.trim();
+ ret.put("AUTHTYPE", s.substring(0, s.indexOf(' ')));
+ s = s.substring(s.indexOf(' ')).trim();
+
+ while (s.length() > 0) {
+ String val = null;
+ String key = s.substring(0, s.indexOf('='));
+ s = s.substring(s.indexOf('=') + 1);
+ if (s.charAt(0) == '\"') {
+ s = s.substring(1);
+ val = s.substring(0, s.indexOf('\"'));
+ s = s.substring(s.indexOf('\"') + 1);
+ } else {
+ val = s.indexOf(',') == -1 ? s : s.substring(0, s.indexOf(','));
+ s = s.indexOf(',') == -1 ? "" : s.substring(s.indexOf(',') + 1);
+ }
+ if (s.length() > 0 && s.charAt(0) == ',') s = s.substring(1);
+ s = s.trim();
+ ret.put(key, val);
+ }
+ return ret;
+ }
+
+ private String H(String s) throws IOException {
+ byte[] b = s.getBytes("UTF8");
+ MD5 md5 = new MD5();
+ md5.update(b, 0, b.length);
+ byte[] out = new byte[md5.getDigestSize()];
+ md5.doFinal(out, 0);
+ String ret = "";
+ for(int i=0; i> 4);
+ ret += "0123456789abcdef".charAt(out[i] & 0x0f);
+ }
+ return ret;
+ }
+
+
+ // Proxy ///////////////////////////////////////////////////////////
+
+ /** encapsulates most of the proxy logic; some is shared in HTTP.java */
+ public static class Proxy {
+
+ public String httpProxyHost = null; ///< the HTTP Proxy host to use
+ public int httpProxyPort = -1; ///< the HTTP Proxy port to use
+ public String httpsProxyHost = null; ///< seperate proxy for HTTPS
+ public int httpsProxyPort = -1;
+ public String socksProxyHost = null; ///< the SOCKS Proxy Host to use
+ public int socksProxyPort = -1; ///< the SOCKS Proxy Port to use
+ public String[] excluded = new String[] { }; ///< hosts to be excluded from proxy use; wildcards permitted
+ public JS proxyAutoConfigFunction = null; ///< the PAC script
+
+ public static Proxy detectProxyViaManual() {
+ Proxy ret = new Proxy();
+
+ ret.httpProxyHost = Platform.getEnv("http_proxy");
+ if (ret.httpProxyHost != null) {
+ if (ret.httpProxyHost.startsWith("http://")) ret.httpProxyHost = ret.httpProxyHost.substring(7);
+ if (ret.httpProxyHost.endsWith("/"))
+ ret.httpProxyHost = ret.httpProxyHost.substring(0, ret.httpProxyHost.length() - 1);
+ if (ret.httpProxyHost.indexOf(':') != -1) {
+ ret.httpProxyPort = Integer.parseInt(ret.httpProxyHost.substring(ret.httpProxyHost.indexOf(':') + 1));
+ ret.httpProxyHost = ret.httpProxyHost.substring(0, ret.httpProxyHost.indexOf(':'));
+ } else {
+ ret.httpProxyPort = 80;
+ }
+ }
+
+ ret.httpsProxyHost = Platform.getEnv("https_proxy");
+ if (ret.httpsProxyHost != null) {
+ if (ret.httpsProxyHost.startsWith("https://")) ret.httpsProxyHost = ret.httpsProxyHost.substring(7);
+ if (ret.httpsProxyHost.endsWith("/"))
+ ret.httpsProxyHost = ret.httpsProxyHost.substring(0, ret.httpsProxyHost.length() - 1);
+ if (ret.httpsProxyHost.indexOf(':') != -1) {
+ ret.httpsProxyPort = Integer.parseInt(ret.httpsProxyHost.substring(ret.httpsProxyHost.indexOf(':') + 1));
+ ret.httpsProxyHost = ret.httpsProxyHost.substring(0, ret.httpsProxyHost.indexOf(':'));
+ } else {
+ ret.httpsProxyPort = 80;
+ }
+ }
+
+ ret.socksProxyHost = Platform.getEnv("socks_proxy");
+ if (ret.socksProxyHost != null) {
+ if (ret.socksProxyHost.startsWith("socks://")) ret.socksProxyHost = ret.socksProxyHost.substring(7);
+ if (ret.socksProxyHost.endsWith("/"))
+ ret.socksProxyHost = ret.socksProxyHost.substring(0, ret.socksProxyHost.length() - 1);
+ if (ret.socksProxyHost.indexOf(':') != -1) {
+ ret.socksProxyPort = Integer.parseInt(ret.socksProxyHost.substring(ret.socksProxyHost.indexOf(':') + 1));
+ ret.socksProxyHost = ret.socksProxyHost.substring(0, ret.socksProxyHost.indexOf(':'));
+ } else {
+ ret.socksProxyPort = 80;
+ }
+ }
+
+ String noproxy = Platform.getEnv("no_proxy");
+ if (noproxy != null) {
+ StringTokenizer st = new StringTokenizer(noproxy, ",");
+ ret.excluded = new String[st.countTokens()];
+ for(int i=0; st.hasMoreTokens(); i++) ret.excluded[i] = st.nextToken();
+ }
+
+ if (ret.httpProxyHost == null && ret.socksProxyHost == null) return null;
+ return ret;
+ }
+
+ public static JSScope proxyAutoConfigRootScope = new ProxyAutoConfigRootScope();
+ public static JS getProxyAutoConfigFunction(String url) {
+ try {
+ BufferedReader br = new BufferedReader(new InputStreamReader(new HTTP(url, true).GET()));
+ String s = null;
+ String script = "";
+ while((s = br.readLine()) != null) script += s + "\n";
+ if (Log.on) Log.info(Proxy.class, "successfully retrieved WPAD PAC:");
+ if (Log.on) Log.info(Proxy.class, script);
+
+ // MS CARP hack
+ Vector carpHosts = new Vector();
+ for(int i=0; i= d1 && day <= d2) || (d1 > d2 && (day >= d1 || day <= d2))) ? T : F;
+
+ } while(false); break SUCCESS; } break; }; break; } case 15: { switch(ccSwitch1.charAt(0)) { case 'd': if ("dnsDomainLevels".equals(ccSwitch1)) { if (true) do {
+String s = a0.toString();
+int i = 0;
+while((i = s.indexOf('.', i)) != -1) i++;
+return new Integer(i);
+ } while(false); break SUCCESS; } break; case 'i': if ("isPlainHostName".equals(ccSwitch1)) { if (true) do { return (a0.toString().indexOf('.') == -1) ? Boolean.TRUE : Boolean.FALSE;
+ } while(false); break SUCCESS; } break; }; break; } case 19: { switch(ccSwitch1.charAt(0)) { case 'l': if ("localHostOrDomainIs".equals(ccSwitch1)) { if (true) do {
+return (a0.equals(a1) || (a0.toString().indexOf('.') == -1 && a1.toString().startsWith(a0.toString()))) ? T:F;
+ } while(false); break SUCCESS; } break; }; break; } } /* switch */ } while(false); /* OUTER */
+ return super.callMethod(method, a0, a1, a2, rest, nargs);
+ }
+ private static boolean match(String[] arr, String s, int index) {
+ if (index >= arr.length) return true;
+ for(int i=0; i epoch.
+ time *= 10000; // tenths of a microsecond.
+ // convert to little-endian byte array.
+ byte[] timestamp = new byte[8];
+ for (int i = 0; i < 8; i++) {
+ timestamp[i] = (byte) time;
+ time >>>= 8;
+ }
+ byte[] blob = new byte[blobSignature.length + reserved.length +
+ timestamp.length + clientChallenge.length +
+ unknown1.length + targetInformation.length +
+ unknown2.length];
+ int offset = 0;
+ System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length);
+ offset += blobSignature.length;
+ System.arraycopy(reserved, 0, blob, offset, reserved.length);
+ offset += reserved.length;
+ System.arraycopy(timestamp, 0, blob, offset, timestamp.length);
+ offset += timestamp.length;
+ System.arraycopy(clientChallenge, 0, blob, offset,
+ clientChallenge.length);
+ offset += clientChallenge.length;
+ System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
+ offset += unknown1.length;
+ System.arraycopy(targetInformation, 0, blob, offset,
+ targetInformation.length);
+ offset += targetInformation.length;
+ System.arraycopy(unknown2, 0, blob, offset, unknown2.length);
+ return blob;
+ }
+
+ /**
+ * Calculates the HMAC-MD5 hash of the given data using the specified
+ * hashing key.
+ *
+ * @param data The data for which the hash will be calculated.
+ * @param key The hashing key.
+ *
+ * @return The HMAC-MD5 hash of the given data.
+ */
+ private static byte[] hmacMD5(byte[] data, byte[] key) {
+ byte[] ipad = new byte[64];
+ byte[] opad = new byte[64];
+ for (int i = 0; i < 64; i++) {
+ ipad[i] = (byte) 0x36;
+ opad[i] = (byte) 0x5c;
+ }
+ for (int i = key.length - 1; i >= 0; i--) {
+ ipad[i] ^= key[i];
+ opad[i] ^= key[i];
+ }
+ byte[] content = new byte[data.length + 64];
+ System.arraycopy(ipad, 0, content, 0, 64);
+ System.arraycopy(data, 0, content, 64, data.length);
+ MD5 md5 = new MD5();
+ md5.update(content, 0, content.length);
+ data = new byte[md5.getDigestSize()];
+ md5.doFinal(data, 0);
+ content = new byte[data.length + 64];
+ System.arraycopy(opad, 0, content, 0, 64);
+ System.arraycopy(data, 0, content, 64, data.length);
+ md5 = new MD5();
+ md5.update(content, 0, content.length);
+ byte[] ret = new byte[md5.getDigestSize()];
+ md5.doFinal(ret, 0);
+ return ret;
+ }
+
+ /**
+ * Creates a DES encryption key from the given key material.
+ *
+ * @param bytes A byte array containing the DES key material.
+ * @param offset The offset in the given byte array at which
+ * the 7-byte key material starts.
+ *
+ * @return A DES encryption key created from the key material
+ * starting at the specified offset in the given byte array.
+ */
+ /*
+ private static Key createDESKey(byte[] bytes, int offset) {
+ byte[] keyBytes = new byte[7];
+ System.arraycopy(bytes, offset, keyBytes, 0, 7);
+ byte[] material = new byte[8];
+ material[0] = keyBytes[0];
+ material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1);
+ material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2);
+ material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3);
+ material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4);
+ material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5);
+ material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6);
+ material[7] = (byte) (keyBytes[6] << 1);
+ oddParity(material);
+ return new SecretKeySpec(material, "DES");
+ }
+ */
+
+ /**
+ * Applies odd parity to the given byte array.
+ *
+ * @param bytes The data whose parity bits are to be adjusted for
+ * odd parity.
+ */
+ private static void oddParity(byte[] bytes) {
+ for (int i = 0; i < bytes.length; i++) {
+ byte b = bytes[i];
+ boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^
+ (b >>> 4) ^ (b >>> 3) ^ (b >>> 2) ^
+ (b >>> 1)) & 0x01) == 0;
+ if (needsParity) {
+ bytes[i] |= (byte) 0x01;
+ } else {
+ bytes[i] &= (byte) 0xfe;
+ }
+ }
+ }
+
+ }
+ }
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/net/SOAP.java b/upstream/org.ibex.core/build/java/org/ibex/net/SOAP.java
new file mode 100644
index 0000000..85670a3
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/net/SOAP.java
@@ -0,0 +1,282 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.net;
+
+import java.io.*;
+import java.util.*;
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.crypto.*;
+
+/**
+ * A partial RPC-style SOAP 1.1 client. Implemented from the SOAP 1.1
+ * Spec and Dave Winer's "SOAP for Busy Developers". This class
+ * extends XMLRPC in order to share some networking logic.
+ *
+ * Currently unsupported features/hacks:
+ *
Multi-ref data and circular references
+ *
'Document Style'
+ *
WSDL support
+ *
+ */
+public class SOAP extends XMLRPC {
+
+ /** the desired content of the SOAPAction header */
+ String action = null;
+
+ /** the namespace to use */
+ String nameSpace = null;
+
+ /** When you get a property from an SOAP, it just returns another SOAP with the property name tacked onto methodname. */
+ public Object get(Object name) {
+ return new SOAP(url, (method.equals("") ? "" : method + ".") + name.toString(), this, action, nameSpace); }
+
+
+ // Methods to Recieve and parse SOAP Responses ////////////////////////////////////////////////////
+
+ public void startElement(String name, String[] keys, Object[] vals, int line, int col) {
+
+ content.reset();
+ if (name.equals("SOAP-ENV:Envelope")) return;
+ if (name.equals("SOAP-ENV:Body")) return;
+ if (name.equals("SOAP-ENV:Fault")) fault = true;
+
+ // add a generic struct; we'll change this if our type is different
+ objects.addElement(new JS());
+
+ for(int i=0; i 0 && content.toString().trim().length() > 0) {
+
+ // remove ourselves
+ Object me = objects.elementAt(objects.size() - 1);
+
+ if (fault || me instanceof String) {
+ objects.removeElementAt(objects.size() - 1);
+ objects.addElement(new String(content.getBuf(), 0, content.size()).intern());
+ content.reset();
+
+ } else if (me instanceof byte[]) {
+ objects.removeElementAt(objects.size() - 1);
+ objects.addElement(new Stream.ByteArray(Base64.decode(new String(content.getBuf(), 0, content.size())), null));
+ content.reset();
+
+ } else if (me instanceof Integer) {
+ objects.removeElementAt(objects.size() - 1);
+ objects.addElement(new Integer(new String(content.getBuf(), 0, content.size())));
+ content.reset();
+
+ } else if (me instanceof Boolean) {
+ objects.removeElementAt(objects.size() - 1);
+ String s = new String(content.getBuf(), 0, content.size()).trim();
+ if (s.equals("1") || s.equals("true")) objects.addElement(Boolean.TRUE);
+ else objects.addElement(Boolean.FALSE);
+ content.reset();
+
+ } else if (me instanceof Double) {
+ objects.removeElementAt(objects.size() - 1);
+ objects.addElement(new Double(new String(content.getBuf(), 0, content.size())));
+ content.reset();
+
+ } else {
+ // okay, we got PCDATA for what is supposedly a
+ // struct... somebody's not adding their type info...
+ String s = new String(content.getBuf(), 0, content.size()).trim();
+ boolean hasdot = false;
+ for(int i=0; i 1 ? objects.elementAt(objects.size() - 2) : null;
+
+ // we want to fold stuff back into the fault object
+ if (objects.size() < 2) return;
+
+ // our parent "should" be an aggregate type -- add ourselves to it.
+ if (parent != null && parent instanceof JSArray) {
+ objects.removeElementAt(objects.size() - 1);
+ ((JSArray)parent).addElement(me);
+
+ } else if (parent != null && parent instanceof JS) {
+ objects.removeElementAt(objects.size() - 1);
+ try {
+ ((JS)parent).put(name, me);
+ } catch (JSExn e) {
+ throw new Error("this should never happen");
+ }
+
+ }
+
+ }
+
+ /** Appends the SOAP representation of o to sb */
+ void appendObject(String name, Object o, StringBuffer sb) throws JSExn {
+ if (o instanceof Number) {
+ if ((double)((Number)o).intValue() == ((Number)o).doubleValue()) {
+ sb.append(" <" + name + " xsi:type=\"xsd:int\">");
+ sb.append(((Number)o).intValue());
+ sb.append("" + name + ">\r\n");
+ } else {
+ sb.append(" <" + name + " xsi:type=\"xsd:double\">");
+ sb.append(o);
+ sb.append("" + name + ">\r\n");
+ }
+
+ } else if (o instanceof Boolean) {
+ sb.append(" <" + name + " xsi:type=\"xsd:boolean\">");
+ sb.append(((Boolean)o).booleanValue() ? "true" : "false");
+ sb.append("" + name + ">\r\n");
+
+ } else if (o instanceof Stream) {
+ try {
+ sb.append(" <" + name + " xsi:type=\"SOAP-ENC:base64\">\r\n");
+ InputStream is = ((Stream)o).getInputStream();
+ byte[] buf = new byte[54];
+ while(true) {
+ int numread = is.read(buf, 0, 54);
+ if (numread == -1) break;
+ byte[] writebuf = buf;
+ if (numread < buf.length) {
+ writebuf = new byte[numread];
+ System.arraycopy(buf, 0, writebuf, 0, numread);
+ }
+ sb.append(" ");
+ sb.append(new String(Base64.encode(writebuf)));
+ sb.append("\r\n");
+ }
+ sb.append(((Boolean)o).booleanValue() ? "1" : "0");
+ sb.append("" + name + ">\r\n");
+ } catch (IOException e) {
+ if (Log.on) Log.info(this, "caught IOException while attempting to send a ByteStream via SOAP");
+ if (Log.on) Log.info(this, e);
+ throw new JSExn("caught IOException while attempting to send a ByteStream via SOAP");
+ }
+
+ } else if (o instanceof String) {
+ sb.append(" <" + name + " xsi:type=\"xsd:string\">");
+ String s = (String)o;
+ if (s.indexOf('<') == -1 && s.indexOf('&') == -1) {
+ sb.append(s);
+ } else {
+ char[] cbuf = s.toCharArray();
+ while(true) {
+ int oldi = 0, i=0;
+ while(i < cbuf.length && cbuf[i] != '<' && cbuf[i] != '&') i++;
+ sb.append(cbuf, oldi, i);
+ if (i == cbuf.length) break;
+ if (cbuf[i] == '<') sb.append("<");
+ else if (cbuf[i] == '&') sb.append("&");
+ i = oldi = i + 1;
+ }
+ }
+ sb.append("" + name + ">\r\n");
+
+ } else if (o instanceof JSArray) {
+ JSArray a = (JSArray)o;
+ sb.append(" <" + name + " SOAP-ENC:arrayType=\"xsd:ur-type[" + a.length() + "]\">");
+ for(int i=0; i\r\n");
+
+ } else if (o instanceof JS) {
+ JS j = (JS)o;
+ sb.append(" <" + name + ">");
+ Enumeration e = j.keys();
+ while(e.hasMoreElements()) {
+ Object key = e.nextElement();
+ appendObject((String)key, j.get(key), sb);
+ }
+ sb.append("" + name + ">\r\n");
+
+ }
+ }
+
+ protected String buildRequest(JSArray args) throws JSExn, IOException {
+ // build up the request
+ StringBuffer content = new StringBuffer();
+ content.append("SOAPAction: " + action + "\r\n\r\n");
+ content.append("\r\n");
+ content.append("\r\n");
+ content.append("\r\n");
+ content.append(" <");
+ content.append(method);
+ content.append(nameSpace != null ? " xmlns=\"" + nameSpace + "\"" : "");
+ content.append(">\r\n");
+ if (args.length() > 0) {
+ Enumeration e = ((JS)args.elementAt(0)).keys();
+ while(e.hasMoreElements()) {
+ Object key = e.nextElement();
+ appendObject((String)key, ((JS)args.elementAt(0)).get(key), content);
+ }
+ }
+ content.append(" " + method + ">\r\n");
+ return content.toString();
+ }
+
+ public SOAP(String url, String methodname, String action, String nameSpace) {
+ super(url, methodname);
+ this.action = action;
+ this.nameSpace = nameSpace;
+ }
+ public SOAP(String url, String methodname, SOAP httpSource, String action, String nameSpace) {
+ super(url, methodname, httpSource);
+ this.action = action;
+ this.nameSpace = nameSpace;
+ }
+
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/net/XMLRPC.java b/upstream/org.ibex.core/build/java/org/ibex/net/XMLRPC.java
new file mode 100644
index 0000000..115c4b5
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/net/XMLRPC.java
@@ -0,0 +1,348 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.net;
+
+import java.io.*;
+import java.util.*;
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.crypto.*;
+
+/**
+ * An XML-RPC client implemented as a JavaScript Host Object. See the
+ * Ibex spec for information on its behavior.
+ *
+ * NOTE: this client is EXTREMELY lenient in the responses it will
+ * accept; there are many, many invalid responses that it will
+ * successfully parse and return. Do NOT use this to determine the
+ * validity of your server.
+ *
+ * This client conforms to The
+ * XML-RPC Spec, subject to these limitations:
+ *
+ *
XMLRPC cannot invoke methods that require a argument
+ *
if a return value contains a , it will be returned as a string
+ *
The decision to pass a number as or is based
+ * entirely on whether or not the argument is fractional. Thus, it
+ * is impossible to pass a non-fractional number to an xmlrpc
+ * method that insists on being called with a element. We
+ * hope that most xml-rpc servers will be able to automatically
+ * convert.
+ *
+ */
+public class XMLRPC extends JS {
+
+ public XMLRPC(String url, String method) {
+ this.http = url.startsWith("stdio:") ? HTTP.stdio : new HTTP(url);
+ this.url = url;
+ this.method = method;
+ }
+ public XMLRPC(String url, String method, XMLRPC httpSource) {
+ this.http = httpSource.http; this.url = url; this.method = method; }
+ public Object get(Object name) {
+ return new XMLRPC(url, (method.equals("") ? "" : method + ".") + name.toString(), this); }
+
+
+ /** this holds character content as we read it in -- since there is only one per instance, we don't support mixed content */
+ protected AccessibleCharArrayWriter content = new AccessibleCharArrayWriter(100);
+ protected String url = null; ///< the url to connect to
+ protected String method = null; ///< the method name to invoke on the remove server
+ protected HTTP http = null; ///< the HTTP connection to use
+ private Hash tracker; ///< used to detect multi-ref data
+ protected boolean fault = false; ///< True iff the return value is a fault (and should be thrown as an exception)
+
+
+ /** The object stack. As we process xml elements, pieces of the
+ * return value are pushed onto and popped off of this stack.
+ *
+ * The general protocol is that any time a <value> tag is
+ * encountered, an empty String ("") is pushed onto the stack. If
+ * the <value/> node has content (either an anonymous
+ * string or some other XML node), that content replaces the
+ * empty string.
+ *
+ * If an <array> tag is encountered, a null is pushed onto the
+ * stack. When a </data> is encountered, we search back on the
+ * stack to the last null, replace it with a NativeJSArray, and
+ * insert into it all elements above it on the stack.
+ *
+ * If a <struct> tag is encountered, a JSect is pushed
+ * onto the stack. If a <name> tag is encountered, its CDATA is
+ * pushed onto the stack. When a </member> is encountered, the
+ * name (second element on stack) and value (top of stack) are
+ * popped off the stack and inserted into the struct (third
+ * element on stack).
+ */
+ protected Vec objects = null;
+
+
+ // Recieve ////////////////////////////////////////////////////////////////
+
+ private class Helper extends XML {
+ public Helper() { super(BUFFER_SIZE); }
+
+ public void startElement(XML.Element c) {
+ content.reset();
+final String ccSwitch0 = (String)(c.getLocalName()); SUCCESS:do { switch(ccSwitch0.length()) {
+case 5: { switch(ccSwitch0.charAt(0)) { case 'a': if ("array".equals(ccSwitch0)) { if (true) do { objects.setElementAt(null, objects.size() - 1);
+ } while(false); break SUCCESS; } break; case 'f': if ("fault".equals(ccSwitch0)) { if (true) do { fault = true;
+ } while(false); break SUCCESS; } break; case 'v': if ("value".equals(ccSwitch0)) { if (true) do { objects.addElement("");
+ } while(false); break SUCCESS; } break; }; break; } case 6: { switch(ccSwitch0.charAt(0)) { case 's': if ("struct".equals(ccSwitch0)) { if (true) do { objects.setElementAt(new JS(), objects.size() - 1);
+ } while(false); break SUCCESS; } break; }; break; } } /* switch */ } while(false); /* OUTER */
+ }
+
+ public void endElement(XML.Element c) {
+final String ccSwitch1 = (String)(c.getLocalName()); SUCCESS:do { switch(ccSwitch1.length()) {
+case 2: { switch(ccSwitch1.charAt(0)) { case 'i': if ("i4".equals(ccSwitch1)) { if (true) do { objects.setElementAt(new Integer(new String(content.getBuf(), 0, content.size())), objects.size() - 1);
+ } while(false); break SUCCESS; } break; }; break; } case 3: { switch(ccSwitch1.charAt(0)) { case 'i': if ("int".equals(ccSwitch1)) { if (true) do { objects.setElementAt(new Integer(new String(content.getBuf(), 0, content.size())), objects.size() - 1);
+ } while(false); break SUCCESS; } break; }; break; } case 4: { switch(ccSwitch1.charAt(0)) { case 'd': if ("data".equals(ccSwitch1)) { if (true) do {
+int i;
+for(i=objects.size() - 1; objects.elementAt(i) != null; i--);
+JSArray arr = new JSArray();
+try {
+for(int j = i + 1; j 0) s = s.substring(i);
+
+try {
+JSDate nd = new JSDate();
+double date = JSDate.date_msecFromDate(Double.valueOf(s.substring(0, 4)).doubleValue(),
+Double.valueOf(s.substring(4, 6)).doubleValue() - 1,
+Double.valueOf(s.substring(6, 8)).doubleValue(),
+Double.valueOf(s.substring(9, 11)).doubleValue(),
+Double.valueOf(s.substring(12, 14)).doubleValue(),
+Double.valueOf(s.substring(15, 17)).doubleValue(),
+(double)0
+);
+nd.setTime(JSDate.internalUTC(date));
+objects.setElementAt(nd, objects.size() - 1);
+
+} catch (Exception e) {
+throw new RuntimeException("ibex.net.rpc.xml.recieve.malformedDateTag" +
+"the server sent a tag which was malformed: " + s);
+}
+ } while(false); break SUCCESS; } break; }; break; } } /* switch */ } while(false); /* OUTER */
+ content.reset();
+ }
+
+ public void characters(char[] ch, int start, int length) {
+ try { content.write(ch, start, length); }
+ catch (Exception e) {
+ if (Log.on) Log.info(this, "Exception in XMLRPC.content() -- this should never happen");
+ if (Log.on) Log.info(this, e);
+ }
+ }
+
+ public void whitespace(char[] ch, int start, int length) {}
+ }
+
+ // Send ///////////////////////////////////////////////////////////////////////////
+
+ protected String buildRequest(JSArray args) throws JSExn, IOException {
+ StringBuffer content = new StringBuffer();
+ content.append("\r\n");
+ content.append("\n");
+ content.append(" \n");
+ content.append(" ");
+ content.append(method);
+ content.append("\n");
+ content.append(" \n");
+ for(int i=0; i\n");
+ appendObject(args.elementAt(i), content);
+ content.append(" \n");
+ }
+ content.append(" \n");
+ content.append(" ");
+ return content.toString();
+ }
+
+ /** Appends the XML-RPC representation of o to sb */
+ void appendObject(Object o, StringBuffer sb) throws JSExn {
+
+ if (o == null) {
+ throw new JSExn("attempted to send a null value via XML-RPC");
+
+ } else if (o instanceof Number) {
+ if ((double)((Number)o).intValue() == ((Number)o).doubleValue()) {
+ sb.append(" ");
+ sb.append(((Number)o).intValue());
+ sb.append("\n");
+ } else {
+ sb.append(" ");
+ sb.append(o);
+ sb.append("\n");
+ }
+
+ } else if (o instanceof Boolean) {
+ sb.append(" ");
+ sb.append(((Boolean)o).booleanValue() ? "1" : "0");
+ sb.append("\n");
+
+ } else if (o instanceof Stream) {
+ try {
+ sb.append(" \n");
+ InputStream is = ((Stream)o).getInputStream();
+ byte[] buf = new byte[54];
+ while(true) {
+ int numread = is.read(buf, 0, 54);
+ if (numread == -1) break;
+ byte[] writebuf = buf;
+ if (numread < buf.length) {
+ writebuf = new byte[numread];
+ System.arraycopy(buf, 0, writebuf, 0, numread);
+ }
+ sb.append(" ");
+ sb.append(new String(Base64.encode(writebuf)));
+ sb.append("\n");
+ }
+ sb.append("\n \n");
+ } catch (IOException e) {
+ if (Log.on) Log.info(this, "caught IOException while attempting to send a ByteStream via XML-RPC");
+ if (Log.on) Log.info(this, e);
+ throw new JSExn("caught IOException while attempting to send a ByteStream via XML-RPC");
+ }
+
+ } else if (o instanceof String) {
+ sb.append(" ");
+ String s = (String)o;
+ if (s.indexOf('<') == -1 && s.indexOf('&') == -1) {
+ sb.append(s);
+ } else {
+ char[] cbuf = s.toCharArray();
+ int oldi = 0, i=0;
+ while(true) {
+ while(i < cbuf.length && cbuf[i] != '<' && cbuf[i] != '&') i++;
+ sb.append(cbuf, oldi, i - oldi);
+ if (i >= cbuf.length) break;
+ if (cbuf[i] == '<') sb.append("<");
+ else if (cbuf[i] == '&') sb.append("&");
+ i = oldi = i + 1;
+ if (i >= cbuf.length) break;
+ }
+ }
+ sb.append("\n");
+
+ } else if (o instanceof JSDate) {
+ sb.append(" ");
+ java.util.Date d = new java.util.Date(((JSDate)o).getRawTime());
+ sb.append(d.getYear() + 1900);
+ if (d.getMonth() + 1 < 10) sb.append('0');
+ sb.append(d.getMonth() + 1);
+ if (d.getDate() < 10) sb.append('0');
+ sb.append(d.getDate());
+ sb.append('T');
+ if (d.getHours() < 10) sb.append('0');
+ sb.append(d.getHours());
+ sb.append(':');
+ if (d.getMinutes() < 10) sb.append('0');
+ sb.append(d.getMinutes());
+ sb.append(':');
+ if (d.getSeconds() < 10) sb.append('0');
+ sb.append(d.getSeconds());
+ sb.append("\n");
+
+ } else if (o instanceof JSArray) {
+ if (tracker.get(o) != null) throw new JSExn("attempted to send multi-ref data structure via XML-RPC");
+ tracker.put(o, Boolean.TRUE);
+ sb.append(" \n");
+ JSArray a = (JSArray)o;
+ for(int i=0; i\n");
+
+ } else if (o instanceof JS) {
+ if (tracker.get(o) != null) throw new JSExn("attempted to send multi-ref data structure via XML-RPC");
+ tracker.put(o, Boolean.TRUE);
+ JS j = (JS)o;
+ sb.append(" \n");
+ Enumeration e = j.keys();
+ while(e.hasMoreElements()) {
+ Object key = e.nextElement();
+ sb.append(" " + key + "\n");
+ appendObject(j.get(key), sb);
+ sb.append(" \n");
+ }
+ sb.append(" \n");
+
+ } else {
+ throw new JSExn("attempt to send object of type " + o.getClass().getName() + " via XML-RPC");
+
+ }
+ }
+
+
+ // Call Sequence //////////////////////////////////////////////////////////////////////////
+
+ public final Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ JSArray args = new JSArray();
+ for(int i=0; i> 16, (rgb & 0x0000ff00) >> 8, rgb & 0x000000ff));
+ g.setColor(new java.awt.Color(0x0, 0x0, 0x0));
+ g.fillRect(cx1, cy1, cx2 - cx1, cy2 - cy1);
+
+ // blacken the area we want the glyph to cover
+ g.setPaintMode();
+ g.setClip(cx1, cy1, cx2 - cx1, cy2 - cy1);
+ g.drawImage(((AWTGlyph)source).getImage(), dx, dy, null);
+ g.setClip(0, 0, i.getWidth(null), i.getHeight(null));
+
+ // XOR back, turning black into the chosen rgb color
+ g.setXORMode(new java.awt.Color((rgb & 0x00ff0000) >> 16, (rgb & 0x0000ff00) >> 8, rgb & 0x000000ff));
+ g.setColor(new java.awt.Color(0x0, 0x0, 0x0));
+ g.fillRect(cx1, cy1, cx2 - cx1, cy2 - cy1);
+
+ // restore the graphics context
+ g.setPaintMode();
+ }
+
+ // FIXME: try to use os acceleration
+ public void fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, int argb) {
+ g.setColor(new java.awt.Color((argb & 0x00FF0000) >> 16, (argb & 0x0000FF00) >> 8, (argb & 0x000000FF)));
+ if (x1 == x3 && x2 == x4) {
+ g.fillRect(x1, y1, x4 - x1, y2 - y1);
+ } else for(int y=y1; y _x2) { int _x0 = _x1; _x1 = _x2; _x2 = _x0; }
+ g.fillRect(_x1, _y1, _x2 - _x1, _y2 - _y1);
+ }
+ }
+ }
+
+
+ protected static class AWTSurface extends Surface.DoubleBufferedSurface
+ implements MouseListener, MouseMotionListener, KeyListener, ComponentListener, WindowListener {
+
+ public void blit(PixelBuffer s, int sx, int sy, int dx, int dy, int dx2, int dy2) {
+ insets = (frame == null ? window : frame).getInsets();
+ window.getGraphics().drawImage(((AWTPixelBuffer)s).i,
+ dx + insets.left,
+ dy + insets.top,
+ dx2 + insets.left,
+ dy2 + insets.top,
+ sx, sy, sx + (dx2 - dx), sy + (dy2 - dy), null);
+ }
+
+ /** if (component instanceof Frame) then frame == window else frame == null */
+ Frame frame = null;
+ Window window = null;
+
+ /** our component's insets */
+ protected Insets insets = new Insets(0, 0, 0, 0);
+
+ /** some JDKs let us recycle a single Dimension object when calling getSize() */
+ Dimension singleSize = new Dimension();
+
+ public void toBack() { if (window != null) window.toBack(); }
+ public void toFront() { if (window != null) window.toFront(); }
+ public void setLocation() { window.setLocation(root.x, root.y); }
+ public void setTitleBarText(String s) { if (frame != null) frame.setTitle(s); }
+ public void setIcon(Picture i) { if (frame != null) frame.setIconImage(((AWTPicture)i).i); }
+ public void _setSize(int width, int height) { window.setSize(width + (insets.left + insets.right), height + (insets.top + insets.bottom)); }
+ public void setInvisible(boolean b) { window.setVisible(!b); }
+ protected void _setMinimized(boolean b) { if (Log.on) Log.info(this, "JDK 1.1 platforms cannot minimize or unminimize windows"); }
+ protected void _setMaximized(boolean b) {
+ if (!b) {
+ if (Log.on) Log.info(this, "JDK 1.1 platforms cannot unmaximize windows");
+ return;
+ }
+ window.setLocation(new Point(0, 0));
+ window.setSize(Toolkit.getDefaultToolkit().getScreenSize());
+ }
+
+ class InnerFrame extends Frame {
+ public InnerFrame() throws java.lang.UnsupportedOperationException { }
+ public Dimension getMinimumSize() {
+ return new Dimension(root == null ? 0 : root.minwidth, root == null ? 0 : root.minheight); }
+ public void update(Graphics gr) { paint(gr); }
+ public void paint(Graphics gr) {
+ Rectangle r = gr.getClipBounds();
+
+ // ugly hack for Java1.4 dynamicLayout on Win32 -- this catches expansions during smooth resize
+ int newwidth = Math.max(r.x - insets.left + r.width, root.width);
+ int newheight = Math.max(r.y - insets.top + r.height, root.height);
+ if (newwidth > root.width || newheight > root.height)
+ componentResized(window.getWidth() - insets.left - insets.right,
+ window.getHeight() - insets.top - insets.bottom);
+
+ Dirty(r.x - insets.left, r.y - insets.top, r.width, r.height);
+ }
+ }
+
+ class InnerWindow extends Window {
+ public InnerWindow() throws java.lang.UnsupportedOperationException { super(new Frame()); }
+ public Dimension getMinimumSize() {
+ return new Dimension(root == null ? 0 : root.minwidth, root == null ? 0 : root.minheight); }
+ public void update(Graphics gr) { paint(gr); }
+ public void paint(Graphics gr) {
+ Rectangle r = gr.getClipBounds();
+ Dirty(r.x - insets.left, r.y - insets.top, r.width, r.height);
+ }
+ }
+
+ public void setMinimumSize(int minx, int miny, boolean resizable) { if (frame != null) frame.setResizable(resizable); }
+
+ private int oldfill = 0x0;
+ public void render() {
+ // useful optimizatin;
+ if (oldfill != root.fillcolor) {
+ oldfill = root.fillcolor;
+ window.setBackground((root.fillcolor & 0xFF000000) == 0 ?
+ java.awt.Color.white :
+ new java.awt.Color((root.fillcolor >> 16) & 0xff,
+ (root.fillcolor >> 8) & 0xff,
+ (root.fillcolor) & 0xff));
+ }
+ super.render();
+ }
+
+ AWTSurface(Box root, boolean framed) {
+ super(root);
+ try {
+ if (framed) window = frame = new InnerFrame();
+ else window = new InnerWindow();
+
+ // this is here to catch HeadlessException on jdk1.4
+ } catch (java.lang.UnsupportedOperationException e) {
+ if (Log.on) Log.info(this, "Exception thrown in AWTSurface$InnerFrame() -- this should never happen");
+ if (Log.on) Log.info(this, e);
+ }
+
+ insets = window.getInsets();
+
+ window.addMouseListener(this);
+ window.addKeyListener(this);
+ window.addComponentListener(this);
+ window.addMouseMotionListener(this);
+ window.addWindowListener(this);
+
+ // IMPORTANT: this must be called before render() to ensure
+ // that our peer has been created
+ makeVisible();
+ }
+
+ protected void makeVisible() { window.setVisible(true); }
+
+ public void _dispose() {
+ window.removeMouseListener(this);
+
+ // removed to work around a jdk1.3 bug
+ /* window.removeKeyListener(this); */
+
+ window.removeComponentListener(this);
+ window.removeMouseMotionListener(this);
+ window.removeWindowListener(this);
+ window.dispose();
+ }
+
+ public void syncCursor() {
+ if (cursor.equals("crosshair")) window.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
+ else if (cursor.equals("east")) window.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
+ else if (cursor.equals("move")) window.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+ else if (cursor.equals("north")) window.setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
+ else if (cursor.equals("northeast")) window.setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR));
+ else if (cursor.equals("northwest")) window.setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR));
+ else if (cursor.equals("south")) window.setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
+ else if (cursor.equals("southeast")) window.setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
+ else if (cursor.equals("southwest")) window.setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR));
+ else if (cursor.equals("text")) window.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
+ else if (cursor.equals("west")) window.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
+ else if (cursor.equals("wait")) window.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ else if (cursor.equals("hand")) window.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+ else window.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }
+
+ // AWT Message translation ////////////////////////////////////////////////////////////////
+
+ // these functions are all executed in the AWT thread, not the
+ // MessageQueue thread. As a result, they must be *extremely*
+ // careful about invoking methods on instances of Box. Currently,
+ // they should only enqueue messages, use Box.whoIs()
+ // (unsynchronized but thought to be safe), and modify members of
+ // Surface.
+
+ public void componentHidden(ComponentEvent e) { }
+ public void componentShown(ComponentEvent e) { }
+ public void windowOpened(WindowEvent e) { }
+ public void windowClosed(WindowEvent e) { }
+ public void windowClosing(WindowEvent e) { Close(); }
+ public void windowIconified(WindowEvent e) { Minimized(true); }
+ public void windowDeiconified(WindowEvent e) { dirty(0, 0, root.width, root.height); Minimized(false); }
+ public void windowActivated(WindowEvent e) { Focused(true); }
+ public void windowDeactivated(WindowEvent e) { Focused(false); }
+ public void componentMoved(ComponentEvent e) { PosChange(window.getLocation().x + insets.left, window.getLocation().y + insets.top); }
+
+ public void componentResized(ComponentEvent e) {
+ // we have to periodically do this; I don't know why
+ insets = window.getInsets();
+ componentResized(window.getWidth() - insets.left - insets.right, window.getHeight() - insets.top - insets.bottom);
+ }
+
+ public void componentResized(int newwidth, int newheight) { SizeChange(newwidth, newheight); }
+
+ public void keyTyped(KeyEvent k) { }
+ public void keyPressed(KeyEvent k) { KeyPressed(translateKey(k)); }
+ public void keyReleased(KeyEvent k) { KeyReleased(translateKey(k)); }
+ public void mouseExited(MouseEvent m) { mouseMoved(m); }
+ public void mouseEntered(MouseEvent m) { mouseMoved(m); }
+ public void mouseDragged(MouseEvent m) { mouseMoved(m); }
+ public void mouseMoved(MouseEvent m) {
+
+ // ugly hack for Java1.4 dynamicLayout on Win32 -- this catches contractions during smooth resize
+ int newwidth = window.getWidth() - insets.left - insets.right;
+ int newheight = window.getHeight() - insets.top - insets.bottom;
+ if (newwidth != root.width || newheight != root.height) componentResized(newwidth, newheight);
+
+ Move(m.getX() - insets.left, m.getY() - insets.top);
+ }
+ public void mousePressed(MouseEvent m) { Press(modifiersToButtonNumber(m.getModifiers())); }
+ public void mouseReleased(MouseEvent m) { Release(modifiersToButtonNumber(m.getModifiers())); }
+ public void mouseClicked(MouseEvent m) {
+ if (m.getClickCount() == 2) DoubleClick(modifiersToButtonNumber(m.getModifiers()));
+ else Click(modifiersToButtonNumber(m.getModifiers()));
+ }
+
+ String translateKey(KeyEvent k) {
+ switch (k.getKeyCode()) {
+ case KeyEvent.VK_ALT: return "alt";
+ case KeyEvent.VK_BACK_SPACE: return "back_space";
+ case KeyEvent.VK_CONTROL: return "control";
+ case KeyEvent.VK_DELETE: return "delete";
+ case KeyEvent.VK_DOWN: return "down";
+ case KeyEvent.VK_END: return "end";
+ case KeyEvent.VK_ENTER: return "enter";
+ case KeyEvent.VK_ESCAPE: return "escape";
+ case KeyEvent.VK_F1: return "f1";
+ case KeyEvent.VK_F10: return "f10";
+ case KeyEvent.VK_F11: return "f11";
+ case KeyEvent.VK_F12: return "f12";
+ case KeyEvent.VK_F2: return "f2";
+ case KeyEvent.VK_F3: return "f3";
+ case KeyEvent.VK_F4: return "f4";
+ case KeyEvent.VK_F5: return "f5";
+ case KeyEvent.VK_F6: return "f6";
+ case KeyEvent.VK_F7: return "f7";
+ case KeyEvent.VK_F8: return "f8";
+ case KeyEvent.VK_F9: return "f9";
+ case KeyEvent.VK_HOME: return "home";
+ case KeyEvent.VK_INSERT: return "insert";
+ case KeyEvent.VK_LEFT: return "left";
+ case KeyEvent.VK_META: return "alt";
+ case KeyEvent.VK_PAGE_DOWN: return "page_down";
+ case KeyEvent.VK_PAGE_UP: return "page_up";
+ case KeyEvent.VK_PAUSE: return "pause";
+ case KeyEvent.VK_PRINTSCREEN: return "printscreen";
+ case KeyEvent.VK_RIGHT: return "right";
+ case KeyEvent.VK_SHIFT: return "shift";
+ case KeyEvent.VK_TAB: return "tab";
+ case KeyEvent.VK_UP: return "up";
+ default:
+ char c = k.getKeyChar();
+ if (c >= 1 && c <= 26) c = (char)('a' + c - 1);
+ return String.valueOf(c);
+ }
+ }
+ }
+
+ protected void _decodeJPEG(InputStream is, Picture p) {
+ try {
+ Image i = Toolkit.getDefaultToolkit().createImage(InputStreamToByteArray.convert(is));
+ MediaTracker mediatracker = new MediaTracker(new Canvas());
+ mediatracker.addImage(i, 1);
+ try { mediatracker.waitForAll(); } catch (InterruptedException e) { }
+ mediatracker.removeImage(i);
+ final int width = i.getWidth(null);
+ final int height = i.getHeight(null);
+ final int[] data = new int[width * height];
+ PixelGrabber pg = new PixelGrabber(i, 0, 0, width, height, data, 0, width);
+ pg.grabPixels();
+ if ((pg.getStatus() & ImageObserver.ABORT) != 0)
+ Log.info(this, "PixelGrabber reported an error while decoding JPEG image");
+ p.width = width;
+ p.height = height;
+ p.data = data;
+ } catch (Exception e) {
+ Log.info(this, "Exception caught while decoding JPEG image");
+ Log.info(this, e);
+ }
+ }
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/plat/JVM.java b/upstream/org.ibex.core/build/java/org/ibex/plat/JVM.java
new file mode 100644
index 0000000..4bc76c5
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/plat/JVM.java
@@ -0,0 +1,11 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.plat;
+
+import org.ibex.graphics.*;
+import org.ibex.core.*;
+import org.ibex.net.*;
+
+/** common superclass for all platforms that run in a "real" JVM */
+public abstract class JVM extends Platform {
+
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/plat/Java2.java b/upstream/org.ibex.core/build/java/org/ibex/plat/Java2.java
new file mode 100644
index 0000000..f575b32
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/plat/Java2.java
@@ -0,0 +1,238 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.plat;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import java.net.*;
+import java.util.*;
+import org.ibex.util.*;
+import java.lang.reflect.*;
+import org.ibex.graphics.*;
+import org.ibex.core.*;
+import org.ibex.net.*;
+
+/** Platform class for most reasonable Java1.2+ Java2s */
+public class Java2 extends AWT {
+
+ private boolean isJava14 = false;
+
+ public Java2() {
+ // disable the focus manager so we can intercept the tab key
+ String versionString = System.getProperty("java.version", "");
+ int secondDecimal = versionString.substring(versionString.indexOf('.') + 1).indexOf('.');
+ if (secondDecimal != -1) versionString = versionString.substring(0, versionString.indexOf('.') + 1 + secondDecimal);
+ double version = Double.parseDouble(versionString);
+ if (version >= 1.4) {
+ isJava14 = true;
+ try {
+ Toolkit t = java.awt.Toolkit.getDefaultToolkit();
+ Method m = java.awt.Toolkit.class.getMethod("setDynamicLayout", new Class[] { Boolean.TYPE });
+ m.invoke(t, new Object[] { Boolean.TRUE });
+ } catch (Exception e) {
+ Log.info(this, "Exception while trying to enable AWT Dynamic Layout");
+ Log.info(this, e);
+ }
+ }
+ javax.swing.FocusManager.setCurrentManager(new javax.swing.FocusManager() {
+ public void processKeyEvent(Component focusedComponent, KeyEvent anEvent) { }
+ public void focusPreviousComponent(Component aComponent) { }
+ public void focusNextComponent(Component aComponent) { }
+ });
+ }
+
+ /** this is done with reflection in case a new version of the plugin comes out that doesn't let us pull the sun.plugin.* trick */
+ protected synchronized org.ibex.net.HTTP.Proxy _detectProxy() {
+ return (org.ibex.net.HTTP.Proxy)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() {
+ public Object run() {
+ try {
+ org.ibex.net.HTTP.Proxy pi = new org.ibex.net.HTTP.Proxy();
+
+ Class PluginProxyHandler = Class.forName("sun.plugin.protocol.PluginProxyHandler");
+ Method getDefaultProxyHandler = PluginProxyHandler.getMethod("getDefaultProxyHandler", new Class[] { });
+ Object proxyHandler = getDefaultProxyHandler.invoke(null, new Object[] { });
+
+ Class ProxyHandler = Class.forName("sun.plugin.protocol.ProxyHandler");
+ Method getProxyInfo = ProxyHandler.getMethod("getProxyInfo", new Class[] { URL.class });
+ Object proxyInfo = getProxyInfo.invoke(proxyHandler, new Object[] { new URL("http://www.ibex.org") });
+
+ Class ProxyInfo = Class.forName("sun.plugin.protocol.ProxyInfo");
+
+ if (((Boolean)ProxyInfo.getMethod("isSocksUsed", new Class[] { }).invoke(proxyInfo, new Object[] { })).booleanValue()) {
+ pi.socksProxyHost =
+ (String)ProxyInfo.getMethod("getSocksProxy", new Class[] { }).invoke(proxyInfo, new Object[] { });
+ pi.socksProxyPort =
+ ((Integer)ProxyInfo.getMethod("getSocksPort", new Class[] { }).invoke(proxyInfo, new Object[] { })).intValue();
+ }
+
+ if (((Boolean)ProxyInfo.getMethod("isProxyUsed", new Class[] { }).invoke(proxyInfo, new Object[] { })).booleanValue()) {
+ pi.httpProxyHost =
+ (String)ProxyInfo.getMethod("getProxy", new Class[] { }).invoke(proxyInfo, new Object[] { });
+ pi.httpProxyPort =
+ ((Integer)ProxyInfo.getMethod("getPort", new Class[] { }).invoke(proxyInfo, new Object[] { })).intValue();
+ }
+
+ if (pi.httpProxyHost != null || pi.socksProxyHost != null) return pi;
+ else return null;
+
+ } catch (Throwable e) {
+ if (Log.on) Log.info(this, "No proxy information found in Java Plugin classes");
+ return null;
+ }
+ }});
+ }
+
+ protected PixelBuffer _createPixelBuffer(int w, int h, Surface owner) { return new Java2PixelBuffer(w, h); }
+ protected Surface _createSurface(final Box root, final boolean framed) {
+ return (Surface)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() {
+ public Object run() {
+ if (isJava14) {
+ try {
+ // weaken the binding here to avoid link errors on 1.3.x
+ Class java14SurfaceClass = Class.forName(Java2.class.getName() + "$Java14Surface");
+ Constructor ctor = java14SurfaceClass.getConstructor(new Class[] { Box.class, Boolean.TYPE });
+ return (Surface)ctor.newInstance(new Object[] { root, Boolean.valueOf(framed) });
+ } catch (Exception e) {
+ Log.info(this, e);
+ throw new LinkageError("error: " + e);
+ }
+ } else {
+ return new Java2Surface(root, framed);
+ }
+ }
+ });
+ }
+
+ // Inner Classes //////////////////////////////////////////////////////////////////
+
+ private static Cursor invisibleCursor =
+ Toolkit.getDefaultToolkit().createCustomCursor(new BufferedImage(2, 2, BufferedImage.TYPE_INT_ARGB),
+ new Point(1, 1), "invisible");
+
+ protected static class Java2Surface extends AWTSurface {
+
+ public Java2Surface(Box root, boolean framed) { super(root, framed); }
+
+ protected void _setMinimized(boolean b) {
+ if (frame == null) Log.info(this, "JDK 1.2 can only minimize frames, not windows");
+ else if (b) frame.setState(java.awt.Frame.ICONIFIED);
+ else frame.setState(java.awt.Frame.NORMAL);
+ }
+
+ public void syncCursor() {
+ if (cursor.equals("invisible")) window.setCursor(invisibleCursor);
+ else super.syncCursor();
+ }
+ }
+
+ protected static class Java14Surface extends Java2Surface implements WindowStateListener, MouseWheelListener {
+ public Java14Surface(Box root, boolean framed) {
+ super(root, true);
+ // JDK1.4 doesn't like java.lang.Window's...
+ if (!framed) ((Frame)window).setUndecorated(true);
+ window.addWindowStateListener(this);
+ window.addMouseWheelListener(this);
+ window.setVisible(true);
+ }
+
+ protected void makeVisible() { }
+
+ protected void _setMaximized(boolean m) {
+ if (frame == null) {
+ if (Log.on) Log.info(this, "JDK 1.4 can only maximize frames, not windows");
+ return;
+ }
+ frame.setExtendedState(m ? Frame.MAXIMIZED_BOTH : (minimized ? Frame.ICONIFIED : Frame.NORMAL));
+ }
+ protected void _setMinimized(boolean m) {
+ if (frame == null) {
+ if (Log.on) Log.info(this, "JDK 1.4 can only minimize frames, not windows");
+ return;
+ }
+ frame.setExtendedState(m ? Frame.ICONIFIED : (maximized ? Frame.MAXIMIZED_BOTH : Frame.NORMAL));
+ }
+ public void windowStateChanged(WindowEvent e) {
+ if (e.getOldState() != e.getNewState()) {
+ if ((e.getNewState() & Frame.MAXIMIZED_BOTH) != 0) Maximized(true);
+ else if (((e.getOldState() & Frame.MAXIMIZED_BOTH) != 0) && (e.getNewState() & Frame.MAXIMIZED_BOTH) == 0)
+ Maximized(false);
+ }
+ }
+
+ public void mouseWheelMoved(MouseWheelEvent m) {
+ if (m.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL)
+ VScroll(m.getUnitsToScroll());
+ }
+ }
+
+ protected static class Java2PixelBuffer extends AWTPixelBuffer {
+ private static ColorModel cm = Toolkit.getDefaultToolkit().getColorModel();
+ private static Hashtable emptyHashtable = new Hashtable();
+ private static short[] sbank = null;
+ private static int[] ibank = null;
+ private static byte[] bbank = null;
+ private static int bank_start = 0;
+ private WritableRaster raster = null;
+ private SampleModel sm = null;
+ private DataBuffer buf = null;
+
+ // this doens't seem to work on Windows
+ public void drawGlyph(org.ibex.graphics.Font.Glyph source, int dx, int dy, int cx1, int cy1, int cx2, int cy2, int rgb) {
+ Image i2 = ((AWTGlyph)source).getImage();
+ Graphics2D g2 = (Graphics2D)i.getGraphics();
+ g2.setComposite(AlphaComposite.DstOut);
+ g2.setClip(cx1, cy1, cx2 - cx1, cy2 - cy1);
+ g2.drawImage(i2, dx, dy, null);
+ g2.setComposite(AlphaComposite.DstOver);
+ g2.setColor(new java.awt.Color((rgb & 0x00FF0000) >> 16, (rgb & 0x0000FF00) >> 8, (rgb & 0x000000FF)));
+ g2.fillRect(dx, dy, cx2 - dx, cy2 - dy);
+ g2.drawImage(i2, 0, 0, null);
+ g2.setClip(0, 0, i.getWidth(null), i.getHeight(null));
+ }
+
+ public Java2PixelBuffer(int w, int h) {
+ sm = cm.createCompatibleSampleModel(w, h);
+ int numSamples = w * h * sm.getNumDataElements();
+ if (sm.getDataType() == DataBuffer.TYPE_USHORT) {
+ if (sbank == null || numSamples > 512 * 512 / 3) {
+ buf = new DataBufferUShort(numSamples);
+ } else {
+ if (numSamples > sbank.length - bank_start) {
+ bank_start = 0;
+ sbank = new short[512 * 512];
+ }
+ buf = new DataBufferUShort(sbank, numSamples, bank_start);
+ bank_start += numSamples;
+ }
+ } else if (sm.getDataType() == DataBuffer.TYPE_BYTE) {
+ if (bbank == null || numSamples > 512 * 512 / 3) {
+ buf = new DataBufferByte(numSamples);
+ } else {
+ if (numSamples > bbank.length - bank_start) {
+ bank_start = 0;
+ bbank = new byte[512 * 512];
+ }
+ buf = new DataBufferByte(bbank, numSamples, bank_start);
+ bank_start += numSamples;
+ }
+ } else if (sm.getDataType() == DataBuffer.TYPE_INT) {
+ if (ibank == null || numSamples > 512 * 512 / 3) {
+ buf = new DataBufferInt(numSamples);
+ } else {
+ if (numSamples > ibank.length - bank_start) {
+ bank_start = 0;
+ ibank = new int[512 * 512];
+ }
+ buf = new DataBufferInt(ibank, numSamples, bank_start);
+ bank_start += numSamples;
+ }
+ }
+ raster = Raster.createWritableRaster(sm, buf, null);
+ i = new BufferedImage(cm, raster, false, emptyHashtable);
+ g = i.getGraphics();
+ }
+ }
+
+ protected String getDescriptiveName() { return isJava14 ? "Java 1.4+ JVM" : "Java 1.2+ JVM"; }
+
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/plat/Platform.java b/upstream/org.ibex.core/build/java/org/ibex/plat/Platform.java
new file mode 100644
index 0000000..5ad71c2
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/plat/Platform.java
@@ -0,0 +1,295 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.plat;
+
+import java.lang.reflect.*;
+import java.net.*;
+import java.io.*;
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.graphics.*;
+import org.ibex.core.*;
+import org.ibex.graphics.*;
+import org.ibex.core.*;
+import org.ibex.net.*;
+
+/**
+ * Abstracts away the small irregularities in JVM implementations.
+ *
+ * The default Platform class supports a vanilla JDK 1.1
+ * JVM. Subclasses are provided for other VMs. Methods whose names
+ * start with an underscore are meant to be overridden by
+ * subclasses. If you create a subclass of Platform, you should put
+ * it in the org.ibex.plat package, and add code to this file's static
+ * block to detect the new platform.
+ */
+public abstract class Platform {
+
+ public Platform() { platform = this; }
+
+ // Static Data /////////////////////////////////////////////////////////////////////////////////////
+
+ public static boolean clipboardReadEnabled = false; ///< true iff inside a C-v/A-v/Press3 trap handler
+ public static Platform platform = null; ///< The appropriate Platform object for this JVM
+ public static boolean alreadyDetectedProxy = false; ///< true if proxy autodetection has already been run
+ public static org.ibex.net.HTTP.Proxy cachedProxyInfo = null; ///< the result of proxy autodetection
+ public static String build = "unknown"; ///< the current build
+
+ // VM Detection Logic /////////////////////////////////////////////////////////////////////
+
+ // If you create a new subclass of Platform, you should add logic
+ // here to detect it. Do not reference your class directly -- use
+ // reflection.
+
+ public static void forceLoad() {
+ System.err.print("Detecting JVM...");
+ try {
+ String vendor = System.getProperty("java.vendor", "");
+ String version = System.getProperty("java.version", "");
+ String os_name = System.getProperty("os.name", "");
+ String os_version = System.getProperty("os.version", "");
+ String platform_class = null;
+
+ if (vendor.startsWith("Free Software Foundation")) {
+ if (os_name.startsWith("Window")) platform_class = "Win32";
+ else if (os_name.startsWith("Linux")) platform_class = "Linux";
+ else if (os_name.startsWith("SunOS")) platform_class = "Solaris";
+ else if (os_name.startsWith("Solaris")) platform_class = "Solaris";
+ else if (os_name.startsWith("Darwin")) platform_class = "Darwin";
+ else platform_class = "X11";
+ }
+ else if (!version.startsWith("1.0") && !version.startsWith("1.1")) platform_class = "Java2";
+
+ if (platform_class == null) {
+ Log.error(Platform.class, "Unable to detect JVM");
+ criticalAbort("Unable to detect JVM");
+ }
+
+ System.err.println(" " + os_name + " ==> org.ibex.plat." + platform_class);
+ try {
+ if (platform_class != null) Class.forName("org.ibex.plat." + platform_class).newInstance();
+ } catch (InstantiationException e) {
+ throw e.getCause();
+ }
+
+ String term = Platform.getEnv("TERM");
+ Log.color = term != null && term.length() != 0 && !term.equals("cygwin");
+
+ try {
+ build = (String)Class.forName("org.ibex.Build").getField("build").get(null);
+ Log.diag(Platform.class, "Ibex build: " + build);
+ } catch (ClassNotFoundException cnfe) {
+ Log.warn(Platform.class, "Ibex build: unknown");
+ } catch (Exception e) {
+ Log.info(Platform.class, "exception while detecting build:");
+ Log.info(Platform.class, e);
+ }
+
+ Log.diag(Platform.class, "Ibex VM detection: vendor = " + vendor);
+ Log.diag(Platform.class, " version = " + version);
+ Log.diag(Platform.class, " os = " + os_name + " [version " + os_version + "]");
+ Log.diag(Platform.class, " platform = " + platform.getDescriptiveName());
+ Log.diag(Platform.class, " class = " + platform.getClass().getName());
+ platform.postInit();
+
+ } catch (Throwable e) {
+ Log.error(Platform.class, "Exception while trying to detect JVM");
+ Log.error(Platform.class, e);
+ criticalAbort("Unable to detect JVM");
+ }
+
+ }
+
+
+ // Methods to be Overridden ///////////////////////////////////////////////////////////////////
+
+ protected Surface _createSurface(Box b, boolean framed) { return null; }
+ protected Picture _createPicture(JS r) { return null; }
+ protected PixelBuffer _createPixelBuffer(int w, int h, Surface owner) { return null; }
+ protected Font.Glyph _createGlyph(org.ibex.graphics.Font f, char c) { return new DefaultGlyph(f, c); }
+
+ public static PixelBuffer createPixelBuffer(int w, int h, Surface s) { return platform._createPixelBuffer(w, h, s); }
+ public static Picture createPicture(JS r) { return platform._createPicture(r); }
+ public static Font.Glyph createGlyph(org.ibex.graphics.Font f, char c) { return platform._createGlyph(f, c); }
+ public static Surface createSurface(Box b, boolean framed, boolean refreshable) {
+ Surface ret = platform._createSurface(b, framed);
+ ret.setInvisible(false);
+ if (refreshable) {
+ Surface.allSurfaces.addElement(ret);
+ ret.dirty(0, 0, b.width, b.height);
+ ret.Refresh();
+ }
+ try {
+ if (b.get("titlebar") != null) ret.setTitleBarText((String)b.get("titlebar"));
+ } catch (JSExn e) {
+ Log.warn(Platform.class, e);
+ }
+ return ret;
+ }
+
+ /** a string describing the VM */
+ protected String getDescriptiveName() { return "Generic Java 1.1 VM"; }
+
+ /** invoked after initialization messages have been printed; useful for additional platform detection log messages */
+ protected void postInit() { }
+
+ /** the human-readable name of the key mapped to Ibex's 'alt' key */
+ public static String altKeyName() { return platform._altKeyName(); }
+ protected String _altKeyName() { return "alt"; }
+
+ /** returns the contents of the clipboard */
+ public static Object getClipBoard() { return clipboardReadEnabled ? platform._getClipBoard() : null; }
+ protected String _getClipBoard() { return null; }
+
+ /** sets the contents of the clipboard */
+ public static void setClipBoard(String s) { platform._setClipBoard(s); }
+ protected void _setClipBoard(String s) { }
+
+ /** returns the width of the screen, in pixels */
+ public static int getScreenWidth() { return platform._getScreenWidth(); }
+ protected int _getScreenWidth() { return 640; }
+
+ /** returns the height of the screen, in pixels */
+ public static int getScreenHeight() { return platform._getScreenHeight(); }
+ protected int _getScreenHeight() { return 480; }
+
+ /** used to notify the user of very serious failures; usually used when logging is not working or unavailable */
+ protected void _criticalAbort(String message) { System.exit(-1); }
+ public static void criticalAbort(String message) {
+ Log.info(Platform.class, "Critical Abort:");
+ Log.info(Platform.class, message);
+ platform._criticalAbort(message);
+ }
+
+ /** if true, org.ibex.Surface will generate a Click automatically after a press and a release */
+ public static boolean needsAutoClick() { return platform._needsAutoClick(); }
+ protected boolean _needsAutoClick() { return false; }
+
+ /** if true, org.ibex.Surface will generate a DoubleClick automatically after recieving two clicks in a short period of time */
+ public static boolean needsAutoDoubleClick() { return platform._needsAutoDoubleClick(); }
+ protected boolean _needsAutoDoubleClick() { return false; }
+
+ /** returns true iff the platform has a case-sensitive filesystem */
+ public static boolean isCaseSensitive() { return platform._isCaseSensitive(); }
+ protected boolean _isCaseSensitive() { return true; }
+
+ /** returns an InputStream to the builtin xwar */
+ public static InputStream getBuiltinInputStream() { return platform._getBuiltinInputStream(); }
+ protected InputStream _getBuiltinInputStream() {return getClass().getClassLoader().getResourceAsStream("org/ibex/builtin.jar");}
+
+ /** returns the value of the environment variable key, or null if no such key exists */
+ public static String getEnv(String key) { return platform._getEnv(key); }
+ protected String _getEnv(String key) {
+ try {
+ String os = System.getProperty("os.name").toLowerCase();
+ Process p;
+ if (os.indexOf("windows 9") != -1 || os.indexOf("windows me") != -1) {
+ // hack -- jdk1.2/1.3 on Win32 pop open an ugly DOS box; 1.4 does not
+ if (platform.getClass().getName().endsWith("Java12")) return null;
+ p = Runtime.getRuntime().exec("command.com /c set");
+ } else if (os.indexOf("windows") > -1) {
+ // hack -- jdk1.2/1.3 on Win32 pop open an ugly DOS box; 1.4 does not
+ if (platform.getClass().getName().endsWith("Java12")) return null;
+ p = Runtime.getRuntime().exec("cmd.exe /c set");
+ } else {
+ p = Runtime.getRuntime().exec("env");
+ }
+ BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
+ String s;
+ while ((s = br.readLine()) != null)
+ if (s.startsWith(key + "="))
+ return s.substring(key.length() + 1);
+ } catch (Exception e) {
+ Log.info(this, "Exception while reading from environment:");
+ Log.info(this, e);
+ }
+ return null;
+ }
+
+ /** convert a JPEG into an Image */
+ public static synchronized void decodeJPEG(InputStream is, Picture p) { platform._decodeJPEG(is, p); }
+ protected abstract void _decodeJPEG(InputStream is, Picture p);
+
+ /** displays a platform-specific "open file" dialog and returns the chosen filename, or null if the user hit cancel */
+ protected String _fileDialog(String suggestedFileName, boolean write) { return null; }
+ public static String fileDialog(String suggestedFileName, boolean write) throws org.ibex.js.JSExn {
+ return platform._fileDialog(suggestedFileName, write);
+ }
+
+ /** default implementation is Eric Albert's BrowserLauncher.java */
+ protected void _newBrowserWindow(String url) {
+ try {
+ Class c = Class.forName("edu.stanford.ejalbert.BrowserLauncher");
+ Method m = c.getMethod("openURL", new Class[] { String.class });
+ m.invoke(null, new String[] { url });
+ } catch (Exception e) {
+ Log.warn(this, "exception trying to open a browser window");
+ Log.warn(this, e);
+ }
+ }
+
+ /** opens a new browser window */
+ public static void newBrowserWindow(String url) {
+ if (!(url.startsWith("https://") || url.startsWith("http://") || url.startsWith("ftp://") || url.startsWith("mailto:"))) {
+ Log.info(Platform.class, "ibex.newBrowserWindow() only supports http and https urls");
+ return;
+ }
+ // check the URL for well-formedness, as a defense against buffer overflow attacks
+ // FIXME check URL without using URL class
+ /*
+ try {
+ String u = url;
+ if (u.startsWith("https")) u = "http" + u.substring(5);
+ new URL(u);
+ } catch (MalformedURLException e) {
+ Log.info(Platform.class, "URL " + url + " is not well-formed");
+ Log.info(Platform.class, e);
+ }
+ */
+ Log.info(Platform.class, "newBrowserWindow, url = " + url);
+ platform._newBrowserWindow(url);
+ }
+
+ /** detects proxy settings */
+ protected synchronized org.ibex.net.HTTP.Proxy _detectProxy() { return null; }
+ public static synchronized org.ibex.net.HTTP.Proxy detectProxy() {
+
+ if (cachedProxyInfo != null) return cachedProxyInfo;
+ if (alreadyDetectedProxy) return null;
+ alreadyDetectedProxy = true;
+
+ Log.info(Platform.class, "attempting environment-variable DNS proxy detection");
+ cachedProxyInfo = org.ibex.net.HTTP.Proxy.detectProxyViaManual();
+ if (cachedProxyInfo != null) return cachedProxyInfo;
+
+ Log.info(Platform.class, "attempting " + platform.getClass().getName() + " proxy detection");
+ cachedProxyInfo = platform._detectProxy();
+ if (cachedProxyInfo != null) return cachedProxyInfo;
+
+ return cachedProxyInfo;
+ }
+
+ /** returns a Scheduler instance; used to implement platform-specific schedulers */
+ protected Scheduler _getScheduler() { return new Scheduler(); }
+ public static Scheduler getScheduler() { return platform._getScheduler(); }
+
+ // FEATURE: be more efficient for many of the subclasses
+ public static class DefaultGlyph extends Font.Glyph {
+ private Picture p = null;
+ public DefaultGlyph(org.ibex.graphics.Font f, char c) { super(f, c); }
+ public Picture getPicture() {
+ if (p == null && isLoaded) {
+ Picture p = createPicture(null);
+ p.data = new int[data.length];
+ for(int i=0; i all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.ibex.util;
+
+// FEATURE: private void intersection() { }
+// FEATURE: private void union() { }
+// FEATURE: private void subset() { }
+// FEATURE: grow if we run out of slots
+
+/** a weight-balanced tree with fake leaves */
+public class BalancedTree {
+
+
+ // Instance Variables ///////////////////////////////////////////////////////////////////
+
+ private int root = 0; ///< the slot of the root element
+
+ private int cached_index = -1;
+ private int cached_slot = -1;
+
+ // Public API //////////////////////////////////////////////////////////////////////////
+
+ /** the number of elements in the tree */
+ public final int treeSize() {
+ synchronized(BalancedTree.class) {
+ return root == 0 ? 0 : size[root];
+ }
+ }
+
+ /** clamps index to [0..treeSize()] and inserts object o *before* the specified index */
+ public final void insertNode(int index, Object o) {
+ synchronized(BalancedTree.class) {
+ if(o == null) throw new Error("can't insert nulls in the balanced tree");
+ cached_slot = cached_index = -1;
+ if (index < 0) index = 0;
+ if (index > treeSize()) index = treeSize();
+ int arg = allocateSlot(o);
+ if (root != 0) {
+ insert(index, arg, root, 0, false, false);
+ } else {
+ root = arg;
+ left[arg] = right[arg] = parent[arg] = 0;
+ size[arg] = 1;
+ }
+ }
+ }
+
+ /** clamps index to [0..treeSize()-1] and replaces the object at that index with object o */
+ public final void replaceNode(int index, Object o) {
+ synchronized(BalancedTree.class) {
+ if(o == null) throw new Error("can't insert nulls in the balanced tree");
+ cached_slot = cached_index = -1;
+ if(root == 0) throw new Error("called replaceNode() on an empty tree");
+ if (index < 0) index = 0;
+ if (index >= treeSize()) index = treeSize() - 1;
+ int arg = allocateSlot(o);
+ insert(index, arg, root, 0, true, false);
+ }
+ }
+
+ /** returns the index of o; runs in O((log n)^2) time unless cache hit */
+ public final int indexNode(Object o) {
+ synchronized(BalancedTree.class) {
+ if(o == null) return -1;
+ if (cached_slot != -1 && objects[cached_slot] == o) return cached_index;
+
+ int slot = getSlot(o);
+ if(slot == -1) return -1;
+
+ int index = 0;
+ while(true) {
+ // everything to the left is before us so add that to the index
+ index += sizeof(left[slot]);
+ // we are before anything on the right
+ while(left[parent[slot]] == slot) slot = parent[slot];
+ // we end of the first node who isn't on the left, go to the node that has as its child
+ slot = parent[slot];
+ // if we just processed the root we're done
+ if(slot == 0) break;
+ // count the node we're currently on towards the index
+ index++;
+ }
+ return index;
+ }
+ }
+
+ /** returns the object at index; runs in O(log n) time unless cache hit */
+ public final Object getNode(int index) {
+ synchronized(BalancedTree.class) {
+ if (index == cached_index) return objects[cached_slot];
+
+ if (cached_index != -1) {
+ int distance = Math.abs(index - cached_index);
+ // if the in-order distance between the cached node and the
+ // target node is less than log(n), it's probably faster to
+ // search directly.
+ if ((distance < 16) && ((2 << distance) < treeSize())) {
+ while(cached_index > index) { cached_slot = prev(cached_slot); cached_index--; }
+ while(cached_index < index) { cached_slot = next(cached_slot); cached_index++; }
+ return objects[cached_slot];
+ }
+ }
+ /*
+ cached_index = index;
+ cached_slot = get(index, root);
+ return objects[cached_slot];
+ */
+ return objects[get(index, root)];
+ }
+ }
+
+ /** deletes the object at index, returning the deleted object */
+ public final Object deleteNode(int index) {
+ synchronized(BalancedTree.class) {
+ cached_slot = cached_index = -1;
+ // FIXME: left[], right[], size[], and parent[] aren't getting cleared properly somewhere in here where a node had two children
+ int del = delete(index, root, 0);
+ left[del] = right[del] = size[del] = parent[del] = 0;
+ Object ret = objects[del];
+ objects[del] = null;
+ return ret;
+ }
+ }
+
+ public final void clear() {
+ synchronized(BalancedTree.class) {
+ if(root == 0) return;
+ int i = leftmost(root);
+ do {
+ int next = next(i);
+ objects[i] = null;
+ left[i] = right[i] = size[i] = parent[i] = 0;
+ i = next;
+ } while(i != 0);
+ root = 0;
+ }
+ }
+
+ protected void finalize() { clear(); }
+
+
+ // Node Data /////////////////////////////////////////////////////////////////////////
+
+ private final static int NUM_SLOTS = 64 * 1024;
+ // FEATURE: GROW - private final static int MAX_SLOT_DISTANCE = 32;
+
+ /**
+ * Every object inserted into *any* tree gets a "slot" in this
+ * array. The slot is determined by hashcode modulo the length of
+ * the array, with quadradic probing to resolve collisions. NOTE
+ * that the "slot" of a node is NOT the same as its index.
+ * Furthermore, if an object is inserted into multiple trees, that
+ * object will have multiple slots.
+ */
+ private static Object[] objects = new Object[NUM_SLOTS];
+
+ /// These two arrays hold the left and right children of each
+ /// slot; in other words, left[x] is the *slot* of the left child
+ /// of the node in slot x.
+ ///
+ /// If x has no left child, then left[x] is -1 multiplied by the
+ /// slot of the node that precedes x; if x is the first node, then
+ /// left[x] is 0. The right[] array works the same way.
+ ///
+ private static int[] left = new int[NUM_SLOTS];
+ private static int[] right = new int[NUM_SLOTS];
+
+ /// The parent of this node (0 if it is the root node)
+ private static int[] parent = new int[NUM_SLOTS];
+
+ ///< the number of descendants of this node *including the node itself*
+ private static int[] size = new int[NUM_SLOTS];
+
+
+ // Slot Management //////////////////////////////////////////////////////////////////////
+
+ /** if alloc == false returns the slot holding object o. if alloc is true returns a new slot for obejct o */
+ private int getSlot(Object o, boolean alloc) {
+ // we XOR with our own hashcode so that we don't get tons of
+ // collisions when a single Object is inserted into multiple
+ // trees
+ int dest = Math.abs(o.hashCode() ^ this.hashCode()) % objects.length;
+ Object search = alloc ? null : o;
+ int odest = dest;
+ boolean plus = true;
+ int tries = 1;
+ if(dest == 0) dest=1;
+ while (objects[dest] != search || !(alloc || root(dest) == root)) {
+ dest = Math.abs((odest + (plus ? 1 : -1) * tries * tries) % objects.length);
+ if (dest == 0) dest=1;
+ if (plus) tries++;
+ plus = !plus;
+ // FEATURE: GROW - if(tries > MAX_SLOT_DISTANCE) return -1;
+ }
+ return dest;
+ }
+
+ /** returns the slots holding object o */
+ private int getSlot(Object o) { return getSlot(o,false); }
+
+ /** allocates a new slot holding object o*/
+ private int allocateSlot(Object o) {
+ int slot = getSlot(o, true);
+ // FEATURE: GROW - if(slot == -1) throw new Error("out of slots");
+ objects[slot] = o;
+ return slot;
+ }
+
+
+
+ // Helpers /////////////////////////////////////////////////////////////////////////
+
+ private final int leftmost(int slot) { return left[slot] <= 0 ? slot : leftmost(left[slot]); }
+ private final int rightmost(int slot) { return right[slot] <= 0 ? slot : rightmost(right[slot]); }
+ private final int next(int slot) { return right[slot] <= 0 ? -1 * right[slot] : leftmost(right[slot]); }
+ private final int prev(int slot) { return left[slot] <= 0 ? -1 * left[slot] : rightmost(left[slot]); }
+ private final int sizeof(int slot) { return slot <= 0 ? 0 : size[slot]; }
+ private final int root(int slot) { return parent[slot] == 0 ? slot : root(parent[slot]); }
+
+
+ // Rotation and Balancing /////////////////////////////////////////////////////////////
+
+ // p p
+ // | |
+ // b d
+ // / \ / \
+ // a d < == > b e
+ // / \ / \
+ // c e a c
+ // FIXME might be doing too much work here
+ private void rotate(boolean toTheLeft, int b, int p) {
+ int[] left = toTheLeft ? BalancedTree.left : BalancedTree.right;
+ int[] right = toTheLeft ? BalancedTree.right : BalancedTree.left;
+ int d = right[b];
+ int c = left[d];
+ if (d <= 0) throw new Error("rotation error");
+ left[d] = b;
+ right[b] = c <= 0 ? -d : c;
+
+ parent[b] = d;
+ parent[d] = p;
+ if(c > 0) parent[c] = b;
+ if (p == 0) root = d;
+ else if (left[p] == b) left[p] = d;
+ else if (right[p] == b) right[p] = d;
+ else throw new Error("rotate called with invalid parent");
+ size[b] = 1 + sizeof(left[b]) + sizeof(right[b]);
+ size[d] = 1 + sizeof(left[d]) + sizeof(right[d]);
+ }
+
+ private void balance(int slot, int p) {
+ if (slot <= 0) return;
+ size[slot] = 1 + sizeof(left[slot]) + sizeof(right[slot]);
+ if (sizeof(left[slot]) - 1 > 2 * sizeof(right[slot])) rotate(false, slot, p);
+ else if (sizeof(left[slot]) * 2 < sizeof(right[slot]) - 1) rotate(true, slot, p);
+ }
+
+
+
+ // Insert /////////////////////////////////////////////////////////////////////////
+
+ private void insert(int index, int arg, int slot, int p, boolean replace, boolean wentLeft) {
+ int diff = slot <= 0 ? 0 : index - sizeof(left[slot]);
+ if (slot > 0 && diff != 0) {
+ if (diff < 0) insert(index, arg, left[slot], slot, replace, true);
+ else insert(index - sizeof(left[slot]) - 1, arg, right[slot], slot, replace, false);
+ balance(slot, p);
+ return;
+ }
+
+ if (size[arg] != 0) throw new Error("double insertion");
+
+ // we are replacing an existing node
+ if (replace) {
+ if (diff != 0) throw new Error("this should never happen"); // since we already clamped the index
+ if (p == 0) root = arg;
+ else if (left[p] == slot) left[p] = arg;
+ else if (right[p] == slot) right[p] = arg;
+ left[arg] = left[slot];
+ right[arg] = right[slot];
+ size[arg] = size[slot];
+ parent[arg] = parent[slot];
+ if(left[slot] > 0) parent[left[slot]] = arg;
+ if(right[slot] > 0) parent[right[slot]] = arg;
+ objects[slot] = null;
+ left[slot] = right[slot] = size[slot] = parent[slot] = 0;
+
+ // we become the child of a former leaf
+ } else if (slot <= 0) {
+ int[] left = wentLeft ? BalancedTree.left : BalancedTree.right;
+ int[] right = wentLeft ? BalancedTree.right : BalancedTree.left;
+ left[arg] = slot;
+ left[p] = arg;
+ right[arg] = -1 * p;
+ parent[arg] = p;
+ balance(arg, p);
+
+ // we take the place of a preexisting node
+ } else {
+ left[arg] = left[slot]; // steal slot's left subtree
+ left[slot] = -1 * arg;
+ right[arg] = slot; // make slot our right subtree
+ parent[arg] = parent[slot];
+ parent[slot] = arg;
+ if (slot == root) {
+ root = arg;
+ balance(slot, arg);
+ balance(arg, 0);
+ } else {
+ if (left[p] == slot) left[p] = arg;
+ else if (right[p] == slot) right[p] = arg;
+ else throw new Error("should never happen");
+ balance(slot, arg);
+ balance(arg, p);
+ }
+ }
+ }
+
+
+ // Retrieval //////////////////////////////////////////////////////////////////////
+
+ private int get(int index, int slot) {
+ int diff = index - sizeof(left[slot]);
+ if (diff > 0) return get(diff - 1, right[slot]);
+ else if (diff < 0) return get(index, left[slot]);
+ else return slot;
+ }
+
+
+ // Deletion //////////////////////////////////////////////////////////////////////
+
+ private int delete(int index, int slot, int p) {
+ int diff = index - sizeof(left[slot]);
+ if (diff < 0) {
+ int ret = delete(index, left[slot], slot);
+ balance(slot, p);
+ return ret;
+
+ } else if (diff > 0) {
+ int ret = delete(diff - 1, right[slot], slot);
+ balance(slot, p);
+ return ret;
+
+ // we found the node to delete
+ } else {
+
+ // fast path: it has no children
+ if (left[slot] <= 0 && right[slot] <= 0) {
+ if (p == 0) root = 0;
+ else {
+ int[] side = left[p] == slot ? left : right;
+ side[p] = side[slot]; // fix parent's pointer
+ }
+
+ // fast path: it has no left child, so we replace it with its right child
+ } else if (left[slot] <= 0) {
+ if (p == 0) root = right[slot];
+ else (left[p] == slot ? left : right)[p] = right[slot]; // fix parent's pointer
+ parent[right[slot]] = p;
+ left[leftmost(right[slot])] = left[slot]; // fix our successor-leaf's fake right ptr
+ balance(right[slot], p);
+
+ // fast path; it has no right child, so we replace it with its left child
+ } else if (right[slot] <= 0) {
+ if (p == 0) root = left[slot];
+ else (left[p] == slot ? left : right)[p] = left[slot]; // fix parent's pointer
+ parent[left[slot]] = p;
+ right[rightmost(left[slot])] = right[slot]; // fix our successor-leaf's fake right ptr
+ balance(left[slot], p);
+
+ // node to be deleted has two children, so we replace it with its left child's rightmost descendant
+ } else {
+ int left_childs_rightmost = delete(sizeof(left[slot]) - 1, left[slot], slot);
+ left[left_childs_rightmost] = left[slot];
+ right[left_childs_rightmost] = right[slot];
+ if(left[slot] > 0) parent[left[slot]] = left_childs_rightmost;
+ if(right[slot] > 0) parent[right[slot]] = left_childs_rightmost;
+ parent[left_childs_rightmost] = parent[slot];
+ if (p == 0) root = left_childs_rightmost;
+ else (left[p] == slot ? left : right)[p] = left_childs_rightmost; // fix parent's pointer
+ balance(left_childs_rightmost, p);
+ }
+
+ return slot;
+ }
+ }
+
+ // Debugging ///////////////////////////////////////////////////////////////////////////
+
+ public void printTree() {
+ if(root == 0) System.err.println("Tree is empty");
+ else printTree(root,0,false);
+ }
+ private void printTree(int node,int indent,boolean l) {
+ for(int i=0;i all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.ibex.util;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+
+/** Reads a CAB file structure */
+public class CAB {
+
+ /** reads a CAB file, parses it, and returns an InputStream representing the named file */
+ public static InputStream getFileInputStream(InputStream is, String fileName) throws IOException, EOFException {
+ return getFileInputStream(is, 0, fileName);
+ }
+
+ public static InputStream getFileInputStream(InputStream is, int skipHeaders, String fileName) throws IOException, EOFException {
+ DataInputStream dis = new DataInputStream(is);
+ CFHEADER h = new CFHEADER();
+
+ while (skipHeaders > 0) { CFHEADER.seekMSCF(dis); skipHeaders--; }
+
+ try {
+ h.read(dis);
+ } catch (CFHEADER.BogusHeaderException bhe) {
+ throw new EOFException();
+ }
+
+ for(int i=0; i limit) len = limit;
+ if (limit == 0) return -1;
+ int ret = super.read(b, off, len);
+ limit -= ret;
+ return ret;
+ }
+ }
+
+ /** Encapsulates a CFHEADER entry */
+ public static class CFHEADER {
+ byte[] reserved1 = new byte[4]; // reserved
+ int fileSize = 0; // size of this cabinet file in bytes
+ byte[] reserved2 = new byte[4]; // reserved
+ int offsetOfFirstCFFILEEntry; // offset of the first CFFILE entry
+ byte[] reserved3 = new byte[4]; // reserved
+ byte versionMinor = 3; // cabinet file format version, minor
+ byte versionMajor = 1; // cabinet file format version, major
+ boolean prevCAB = false; // true iff there is a cabinet before this one in a sequence
+ boolean nextCAB = false; // true iff there is a cabinet after this one in a sequence
+ boolean hasReserved = false; // true iff the cab has per-{cabinet, folder, block} reserved areas
+ int setID = 0; // must be the same for all cabinets in a set
+ int indexInCabinetSet = 0; // number of this cabinet file in a set
+ byte perCFFOLDERReservedSize = 0; // (optional) size of per-folder reserved area
+ byte perDatablockReservedSize = 0; // (optional) size of per-datablock reserved area
+ byte[] perCabinetReservedArea = null; // per-cabinet reserved area
+ String previousCabinet = null; // name of previous cabinet file in a set
+ String previousDisk = null; // name of previous disk in a set
+ String nextCabinet = null; // name of next cabinet in a set
+ String nextDisk = null; // name of next disk in a set
+
+ CFFOLDER[] folders = new CFFOLDER[0];
+ CFFILE[] files = new CFFILE[0];
+
+ int readCFFOLDERs = 0; // the number of folders read in so far
+ int readCFFILEs = 0; // the number of folders read in so far
+
+ public void print(PrintStream ps) {
+ ps.println("CAB CFFILE CFHEADER v" + ((int)versionMajor) + "." + ((int)versionMinor));
+ ps.println(" total file size = " + fileSize);
+ ps.println(" offset of first file = " + offsetOfFirstCFFILEEntry);
+ ps.println(" total folders = " + folders.length);
+ ps.println(" total files = " + files.length);
+ ps.println(" flags = 0x" +
+ Integer.toString((prevCAB ? 0x1 : 0x0) |
+ (nextCAB ? 0x2 : 0x0) |
+ (hasReserved ? 0x4 : 0x0), 16) + " [ " +
+ (prevCAB ? "prev " : "") +
+ (nextCAB ? "next " : "") +
+ (hasReserved ? "reserve_present " : "") + "]");
+ ps.println(" set id = " + setID);
+ ps.println(" index in set = " + indexInCabinetSet);
+ ps.println(" header reserved area #1 =" +
+ " 0x" + Integer.toString(reserved1[0], 16) +
+ " 0x" + Integer.toString(reserved1[1], 16) +
+ " 0x" + Integer.toString(reserved1[2], 16) +
+ " 0x" + Integer.toString(reserved1[3], 16));
+ ps.println(" header reserved area #2 =" +
+ " 0x" + Integer.toString(reserved2[0], 16) +
+ " 0x" + Integer.toString(reserved2[1], 16) +
+ " 0x" + Integer.toString(reserved2[2], 16) +
+ " 0x" + Integer.toString(reserved2[3], 16));
+ ps.println(" header reserved area #3 =" +
+ " 0x" + Integer.toString(reserved3[0], 16) +
+ " 0x" + Integer.toString(reserved3[1], 16) +
+ " 0x" + Integer.toString(reserved3[2], 16) +
+ " 0x" + Integer.toString(reserved3[3], 16));
+ if (hasReserved) {
+ if (perCabinetReservedArea != null) {
+ ps.print(" per-cabinet reserved area = ");
+ for(int i=0; i 0)
+ dis.readFully(perCabinetReservedArea);
+ }
+
+ try {
+ if (prevCAB) {
+ previousCabinet = readZeroTerminatedString(dis);
+ previousDisk = readZeroTerminatedString(dis);
+ }
+ if (nextCAB) {
+ nextCabinet = readZeroTerminatedString(dis);
+ nextDisk = readZeroTerminatedString(dis);
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new BogusHeaderException();
+ }
+ }
+
+ public static void seekMSCF(DataInputStream dis) throws EOFException, IOException
+ {
+ int state;
+ // skip up to and including the 'MSCF' signature
+ state = 0;
+ while (state != 4) {
+ // M
+ while (state == 0 && dis.readByte() != 0x4D) { }
+ state = 1;
+ // S
+ switch (dis.readByte()) {
+ case 0x53 : state = 2; break;
+ case 0x4D : state = 1; continue;
+ default : state = 0; continue;
+ }
+ // C
+ if (dis.readByte() == 0x43) { state = 3; }
+ else { state = 0; continue; }
+ // F
+ if (dis.readByte() == 0x46) { state = 4; }
+ else { state = 0; }
+ }
+ }
+
+ public static class BogusHeaderException extends IOException {}
+ }
+
+ /** Encapsulates a CFFOLDER entry */
+ public static class CFFOLDER {
+ public static final int COMPRESSION_NONE = 0;
+ public static final int COMPRESSION_MSZIP = 1;
+ public static final int COMPRESSION_QUANTUM = 2;
+ public static final int COMPRESSION_LZX = 3;
+
+ int firstBlockOffset = 0; // offset of first data block within this folder
+ int numBlocks = 0; // number of data blocks
+ int compressionType = 0; // compression type for this folder
+ byte[] reservedArea = null; // per-folder reserved area
+ int indexInCFHEADER = 0; // our index in CFHEADER.folders
+ Vector files = new Vector();
+
+ private CFHEADER header = null;
+
+ public CFFOLDER(CFHEADER header) { this.header = header; }
+
+ public String toString() {
+ return "[ CAB CFFOLDER, " + numBlocks + " data blocks, compression type " +
+ compressionName(compressionType) +
+ ", " + reservedArea.length + " bytes of reserved data ]";
+ }
+
+ public void read(DataInputStream dis) throws IOException, UnsupportedCompressionTypeException {
+ firstBlockOffset = readLittleInt(dis);
+ numBlocks = readLittleShort(dis);
+ compressionType = readLittleShort(dis) & 0x000F;
+ if (compressionType != COMPRESSION_MSZIP) {
+ throw new UnsupportedCompressionTypeException(compressionType);
+ }
+ reservedArea = new byte[header.perCFFOLDERReservedSize];
+ if (reservedArea.length > 0) dis.readFully(reservedArea);
+ indexInCFHEADER = header.readCFFOLDERs++;
+ header.folders[indexInCFHEADER] = this;
+ }
+
+ public static String compressionName(int type) {
+ switch (type) {
+ case COMPRESSION_NONE:
+ return "NONE";
+ case COMPRESSION_MSZIP:
+ return "MSZIP";
+ case COMPRESSION_QUANTUM:
+ return "QUANTUM";
+ case COMPRESSION_LZX:
+ return "LZX";
+ default:
+ return "";
+ }
+ }
+
+ public static class UnsupportedCompressionTypeException extends IOException {
+ private int compressionType;
+
+ UnsupportedCompressionTypeException(int type) {
+ compressionType = type;
+ }
+ public String toString() {
+ return "UnsupportedCompressionTypeException: no support for compression type " + compressionName(compressionType);
+ }
+ }
+ }
+
+ /** Encapsulates a CFFILE entry */
+ public static class CFFILE {
+ int fileSize = 0; // size of this file
+ int uncompressedOffsetInCFFOLDER = 0; // offset of this file within the folder, not accounting for compression
+ int folderIndex = 0; // index of the CFFOLDER we belong to
+ Date date = null; // modification date
+ int attrs = 0; // attrs
+ boolean readOnly = false; // read-only flag
+ boolean hidden = false; // hidden flag
+ boolean system = false; // system flag
+ boolean arch = false; // archive flag
+ boolean runAfterExec = false; // true if file should be run during extraction
+ boolean UTFfileName = false; // true if filename is UTF-encoded
+ String fileName = null; // filename
+ int indexInCFHEADER = 0; // our index in CFHEADER.files
+ CFFOLDER folder = null; // the folder we belong to
+ private CFHEADER header = null;
+ File myFile;
+
+ public CFFILE(CFHEADER header) { this.header = header; }
+
+ public CFFILE(File f, String pathName) throws IOException {
+ fileSize = (int)f.length();
+ folderIndex = 0;
+ date = new java.util.Date(f.lastModified());
+ fileName = pathName;
+ myFile = f;
+ }
+
+ public String toString() {
+ return "[ CAB CFFILE: " + fileName + ", " + fileSize + " bytes [ " +
+ (readOnly ? "readonly " : "") +
+ (system ? "system " : "") +
+ (hidden ? "hidden " : "") +
+ (arch ? "arch " : "") +
+ (runAfterExec ? "run_after_exec " : "") +
+ (UTFfileName ? "UTF_filename " : "") +
+ "]";
+ }
+
+ public void read(DataInputStream dis) throws IOException {
+ fileSize = readLittleInt(dis);
+ uncompressedOffsetInCFFOLDER = readLittleInt(dis);
+ folderIndex = readLittleShort(dis);
+ readLittleShort(dis); // date
+ readLittleShort(dis); // time
+ attrs = readLittleShort(dis);
+ readOnly = (attrs & 0x1) != 0;
+ hidden = (attrs & 0x2) != 0;
+ system = (attrs & 0x4) != 0;
+ arch = (attrs & 0x20) != 0;
+ runAfterExec = (attrs & 0x40) != 0;
+ UTFfileName = (attrs & 0x80) != 0;
+ fileName = readZeroTerminatedString(dis);
+
+ indexInCFHEADER = header.readCFFILEs++;
+ header.files[indexInCFHEADER] = this;
+ folder = header.folders[folderIndex];
+ folder.files.addElement(this);
+ }
+ }
+
+
+
+
+ // Compressing Input and Output Streams ///////////////////////////////////////////////
+
+ /** an InputStream that decodes CFDATA blocks belonging to a CFFOLDER */
+ private static class CFFOLDERInputStream extends InputStream {
+ CFFOLDER folder;
+ DataInputStream dis;
+ InputStream iis = null;
+
+ byte[] compressed = new byte[128 * 1024];
+ byte[] uncompressed = new byte[256 * 1024];
+
+ public CFFOLDERInputStream(CFFOLDER f, DataInputStream dis) {
+ this.folder = f;
+ this.dis = dis;
+ }
+
+ InputStream readBlock() throws IOException {
+ int compressedBytes = readLittleShort(dis);
+ int unCompressedBytes = readLittleShort(dis);
+ byte[] reserved = new byte[/*folder.header.perDatablockReservedSize*/0];
+ if (reserved.length > 0) dis.readFully(reserved);
+ if (dis.readByte() != 0x43) throw new CABException("malformed block header");
+ if (dis.readByte() != 0x4B) throw new CABException("malformed block header");
+
+ dis.readFully(compressed, 0, compressedBytes - 2);
+
+ Inflater i = new Inflater(true);
+ i.setInput(compressed, 0, compressedBytes - 2);
+
+ if (unCompressedBytes > uncompressed.length) uncompressed = new byte[unCompressedBytes];
+ try { i.inflate(uncompressed, 0, uncompressed.length);
+ } catch (DataFormatException dfe) {
+ dfe.printStackTrace();
+ throw new CABException(dfe.toString());
+ }
+ return new ByteArrayInputStream(uncompressed, 0, unCompressedBytes);
+ }
+
+ public int available() throws IOException { return iis == null ? 0 : iis.available(); }
+ public void close() throws IOException { iis.close(); }
+ public void mark(int i) { }
+ public boolean markSupported() { return false; }
+ public void reset() { }
+
+ public long skip(long l) throws IOException {
+ if (iis == null) iis = readBlock();
+ int ret = 0;
+ while (l > ret) {
+ long numread = iis.skip(l - ret);
+ if (numread == 0 || numread == -1) iis = readBlock();
+ else ret += numread;
+ }
+ return ret;
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (iis == null) iis = readBlock();
+ int ret = 0;
+ while (len > ret) {
+ int numread = iis.read(b, off + ret, len - ret);
+ if (numread == 0 || numread == -1) iis = readBlock();
+ else ret += numread;
+ }
+ return ret;
+ }
+
+ public int read() throws IOException {
+ if (iis == null) iis = readBlock();
+ int ret = iis.read();
+ if (ret == -1) {
+ iis = readBlock();
+ ret = iis.read();
+ }
+ return ret;
+ }
+ }
+
+
+
+ // Misc Stuff //////////////////////////////////////////////////////////////
+
+ public static String readZeroTerminatedString(DataInputStream dis) throws IOException {
+ int numBytes = 0;
+ byte[] b = new byte[256];
+ while(true) {
+ byte next = dis.readByte();
+ if (next == 0x0) return new String(b, 0, numBytes);
+ b[numBytes++] = next;
+ }
+ }
+
+ public static int readLittleInt(DataInputStream dis) throws IOException {
+ int lowest = (int)(dis.readByte() & 0xff);
+ int low = (int)(dis.readByte() & 0xff);
+ int high = (int)(dis.readByte() & 0xff);
+ int highest = (int)(dis.readByte() & 0xff);
+ return (highest << 24) | (high << 16) | (low << 8) | lowest;
+ }
+
+ public static int readLittleShort(DataInputStream dis) throws IOException {
+ int low = (int)(dis.readByte() & 0xff);
+ int high = (int)(dis.readByte() & 0xff);
+ return (high << 8) | low;
+ }
+
+ public static class CABException extends IOException {
+ public CABException(String s) { super(s); }
+ }
+
+
+ /** scratch space for isToByteArray() */
+ static byte[] workspace = new byte[16 * 1024];
+
+ /** Trivial method to completely read an InputStream */
+ public static synchronized byte[] isToByteArray(InputStream is) throws IOException {
+ int pos = 0;
+ while (true) {
+ int numread = is.read(workspace, pos, workspace.length - pos);
+ if (numread == -1) break;
+ else if (pos + numread < workspace.length) pos += numread;
+ else {
+ pos += numread;
+ byte[] temp = new byte[workspace.length * 2];
+ System.arraycopy(workspace, 0, temp, 0, workspace.length);
+ workspace = temp;
+ }
+ }
+ byte[] ret = new byte[pos];
+ System.arraycopy(workspace, 0, ret, 0, pos);
+ return ret;
+ }
+
+
+}
+
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/Cache.java b/upstream/org.ibex.core/build/java/org/ibex/util/Cache.java
new file mode 100644
index 0000000..af88c88
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/Cache.java
@@ -0,0 +1,126 @@
+// Copyright (C) 2003 Adam Megacz all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+/*
+
+Bug report from a user:
+
+I looked at your Cache.java and tried to make good use of it, but I was
+out of luck - it wouldn't run here. Digging deeper into the code, I came
+across something that might be considered a bug. But maybe it's just a
+feature :-)
+
+
+Starting with an empty cache, Cache.put() immediately followed by
+Cache.get() on same keys / same object will set Node lru back to null in
+Node.remove() which is called in get().
+
+Assuming this put()-get() sequence is fixed, it will fill the cache, but
+lru will remain null.
+
+When cache is filled 100%, we have, at the end of the get(), where
+size>maxSize is checked, a state that mru == lru == n (from
+if(lru==null) thus deleteting the newly inserted object. Oops.
+
+
+Hope I made this clear enough. Maybe it's not a problem in xwt due to a
+different usage scheme of the cache.
+
+
+
+*/
+
+package org.ibex.util;
+
+// FIXME needs to be a weak hash
+
+/**
+ * A Hash table with a fixed size; drops extraneous elements. Uses
+ * LRU strategy.
+ */
+public class Cache extends Hash {
+
+ /** head of list is the mru; tail is the lru */
+ Node mru = null;
+ Node lru = null;
+
+ private int maxSize;
+ private Cache() { }
+ public Cache(int maxSize) {
+ super(maxSize * 2, 3);
+ this.maxSize = maxSize;
+ }
+
+ /** A doubly-linked list */
+ private class Node {
+ final Object val;
+ final Object k1;
+ final Object k2;
+ public Node(Object k1, Object k2, Object val) { this.k1 = k1; this.k2 = k2; this.val = val; }
+ Node next = null;
+ Node prev = null;
+ void remove() {
+ if (this == lru) lru = prev;
+ if (this == mru) mru = next;
+ if (next != null) next.prev = prev;
+ if (prev != null) prev.next = next;
+ next = prev = null;
+ }
+ void placeAfter(Node n) {
+ remove();
+ if (n == null) return;
+ next = n.next;
+ if (n.next != null) n.next.prev = this;
+ n.next = this;
+ prev = n;
+ }
+ void placeBefore(Node n) {
+ remove();
+ if (n == null) return;
+ next = n;
+ prev = n.prev;
+ n.prev = this;
+ if (prev != null) prev.next = this;
+ }
+ }
+
+ public void clear() {
+ lru = null;
+ super.clear();
+ }
+
+ public void remove(Object k1, Object k2) {
+ Object o = super.get(k1, k2);
+ if (o != null) ((Node)o).remove();
+ super.remove(k1, k2);
+ }
+
+ public Object get(Object k1, Object k2) {
+ Node n = (Node)super.get(k1, k2);
+ if (n == null) return null;
+ n.remove();
+ n.placeBefore(mru);
+ mru = n;
+ return n.val;
+ }
+
+ public void put(Object k1, Object k2, Object v) {
+ Node n = new Node(k1, k2, v);
+ if (lru == null) {
+ lru = mru = n;
+ } else {
+ n.placeBefore(mru);
+ mru = n;
+ }
+ if (super.get(k1, k2) != null) remove(k1, k2);
+ super.put(k1, k2, n);
+ if (size > maxSize) remove(lru.k1, lru.k2);
+ }
+
+}
+
+
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/CachedInputStream.java b/upstream/org.ibex.core/build/java/org/ibex/util/CachedInputStream.java
new file mode 100644
index 0000000..a712f84
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/CachedInputStream.java
@@ -0,0 +1,88 @@
+// Copyright (C) 2003 Adam Megacz all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.ibex.util;
+import java.io.*;
+
+// FEATURE: don't use a byte[] if we have a diskCache file
+/**
+ * Wraps around an InputStream, caching the stream in a byte[] as it
+ * is read and permitting multiple simultaneous readers
+ */
+public class CachedInputStream {
+
+ boolean filling = false; ///< true iff some thread is blocked on us waiting for input
+ boolean eof = false; ///< true iff end of stream has been reached
+ byte[] cache = new byte[1024 * 128];
+ int size = 0;
+ final InputStream is;
+ File diskCache;
+
+ public CachedInputStream(InputStream is) { this(is, null); }
+ public CachedInputStream(InputStream is, File diskCache) {
+ this.is = is;
+ this.diskCache = diskCache;
+ }
+ public InputStream getInputStream() throws IOException {
+ if (diskCache != null && diskCache.exists()) return new FileInputStream(diskCache);
+ return new SubStream();
+ }
+
+ public void grow(int newLength) {
+ if (newLength < cache.length) return;
+ byte[] newCache = new byte[cache.length + 2 * (newLength - cache.length)];
+ System.arraycopy(cache, 0, newCache, 0, size);
+ cache = newCache;
+ }
+
+ synchronized void fillCache(int howMuch) throws IOException {
+ if (filling) { try { wait(); } catch (InterruptedException e) { }; return; }
+ filling = true;
+ grow(size + howMuch);
+ int ret = is.read(cache, size, howMuch);
+ if (ret == -1) {
+ eof = true;
+ // FIXME: probably a race here
+ if (diskCache != null && !diskCache.exists())
+ try {
+ File cacheFile = new File(diskCache + ".incomplete");
+ FileOutputStream cacheFileStream = new FileOutputStream(cacheFile);
+ cacheFileStream.write(cache, 0, size);
+ cacheFileStream.close();
+ cacheFile.renameTo(diskCache);
+ } catch (IOException e) {
+ Log.info(this, "exception thrown while writing disk cache");
+ Log.info(this, e);
+ }
+ }
+ else size += ret;
+ filling = false;
+ notifyAll();
+ }
+
+ private class SubStream extends InputStream implements KnownLength {
+ int pos = 0;
+ public int available() { return Math.max(0, size - pos); }
+ public long skip(long n) throws IOException { pos += (int)n; return n; } // FEATURE: don't skip past EOF
+ public int getLength() { return eof ? size : is instanceof KnownLength ? ((KnownLength)is).getLength() : 0; }
+ public int read() throws IOException { // FEATURE: be smarter here
+ byte[] b = new byte[1];
+ int ret = read(b, 0, 1);
+ return ret == -1 ? -1 : b[0]&0xff;
+ }
+ public int read(byte[] b, int off, int len) throws IOException {
+ synchronized(CachedInputStream.this) {
+ while (pos >= size && !eof) fillCache(pos + len - size);
+ if (eof && pos == size) return -1;
+ int count = Math.min(size - pos, len);
+ System.arraycopy(cache, pos, b, off, count);
+ pos += count;
+ return count;
+ }
+ }
+ }
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/Callback.java b/upstream/org.ibex.core/build/java/org/ibex/util/Callback.java
new file mode 100644
index 0000000..471df9b
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/Callback.java
@@ -0,0 +1,15 @@
+// Copyright (C) 2003 Adam Megacz all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.ibex.util;
+
+/** a simple interface for callbacks*/
+public interface Callback {
+
+ public abstract Object call(Object arg);
+
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/CounterEnumeration.java b/upstream/org.ibex.core/build/java/org/ibex/util/CounterEnumeration.java
new file mode 100644
index 0000000..5a01f3e
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/CounterEnumeration.java
@@ -0,0 +1,17 @@
+// Copyright (C) 2004 Adam Megacz all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+package org.ibex.util;
+import java.util.*;
+
+public class CounterEnumeration implements Enumeration {
+ public final int max;
+ private int cur = 0;
+ public CounterEnumeration(int i) { max = i; }
+ public void reset() { cur = 0; }
+ public boolean hasMoreElements() { return cur < max; }
+ public Object nextElement() { return new Integer(cur++); }
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/DirtyList.java b/upstream/org.ibex.core/build/java/org/ibex/util/DirtyList.java
new file mode 100644
index 0000000..0a77a94
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/DirtyList.java
@@ -0,0 +1,181 @@
+// Copyright (C) 2003 Adam Megacz all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.ibex.util;
+
+/**
+ * A general-purpose data structure for holding a list of rectangular
+ * regions that need to be repainted, with intelligent coalescing.
+ *
+ * DirtyList will unify two regions A and B if the smallest rectangle
+ * enclosing both A and B occupies no more than epsilon + Area_A +
+ * Area_B. Failing this, if two corners of A fall within B, A will be
+ * shrunk to exclude the union of A and B.
+ */
+public class DirtyList {
+
+ /** The dirty regions (each one is an int[4]). */
+ private int[][] dirties = new int[10][];
+
+ /** The number of dirty regions */
+ private int numdirties = 0;
+
+ /** See class comment */
+ private static final int epsilon = 50 * 50;
+
+ public int num() { return numdirties; }
+
+ /** grows the array */
+ private void grow() {
+ int[][] newdirties = new int[dirties.length * 2][];
+ System.arraycopy(dirties, 0, newdirties, 0, numdirties);
+ dirties = newdirties;
+ }
+
+ /** Add a new rectangle to the dirty list; returns false if the
+ * region fell completely within an existing rectangle or set of
+ * rectangles (ie did not expand the dirty area)
+ */
+ public synchronized boolean dirty(int x, int y, int w, int h) {
+ if (numdirties == dirties.length) grow();
+
+ // we attempt the "lossless" combinations first
+ for(int i=0; i= cur[0] && y >= cur[1] && x + w <= cur[0] + cur[2] && y + h <= cur[1] + cur[3]) {
+ return false;
+
+ // existing region falls completely within new region
+ } else if (x <= cur[0] && y <= cur[1] && x + w >= cur[0] + cur[2] && y + h >= cur[1] + cur[3]) {
+ dirties[i][2] = 0;
+ dirties[i][3] = 0;
+
+ // left end of new region falls within existing region
+ } else if (x >= cur[0] && x < cur[0] + cur[2] && y >= cur[1] && y + h <= cur[1] + cur[3]) {
+ w = x + w - (cur[0] + cur[2]);
+ x = cur[0] + cur[2];
+ i = -1; continue;
+
+ // right end of new region falls within existing region
+ } else if (x + w > cur[0] && x + w <= cur[0] + cur[2] && y >= cur[1] && y + h <= cur[1] + cur[3]) {
+ w = cur[0] - x;
+ i = -1; continue;
+
+ // top end of new region falls within existing region
+ } else if (x >= cur[0] && x + w <= cur[0] + cur[2] && y >= cur[1] && y < cur[1] + cur[3]) {
+ h = y + h - (cur[1] + cur[3]);
+ y = cur[1] + cur[3];
+ i = -1; continue;
+
+ // bottom end of new region falls within existing region
+ } else if (x >= cur[0] && x + w <= cur[0] + cur[2] && y + h > cur[1] && y + h <= cur[1] + cur[3]) {
+ h = cur[1] - y;
+ i = -1; continue;
+
+ // left end of existing region falls within new region
+ } else if (dirties[i][0] >= x && dirties[i][0] < x + w && dirties[i][1] >= y && dirties[i][1] + dirties[i][3] <= y + h) {
+ dirties[i][2] = dirties[i][2] - (x + w - dirties[i][0]);
+ dirties[i][0] = x + w;
+ i = -1; continue;
+
+ // right end of existing region falls within new region
+ } else if (dirties[i][0] + dirties[i][2] > x && dirties[i][0] + dirties[i][2] <= x + w &&
+ dirties[i][1] >= y && dirties[i][1] + dirties[i][3] <= y + h) {
+ dirties[i][2] = x - dirties[i][0];
+ i = -1; continue;
+
+ // top end of existing region falls within new region
+ } else if (dirties[i][0] >= x && dirties[i][0] + dirties[i][2] <= x + w && dirties[i][1] >= y && dirties[i][1] < y + h) {
+ dirties[i][3] = dirties[i][3] - (y + h - dirties[i][1]);
+ dirties[i][1] = y + h;
+ i = -1; continue;
+
+ // bottom end of existing region falls within new region
+ } else if (dirties[i][0] >= x && dirties[i][0] + dirties[i][2] <= x + w &&
+ dirties[i][1] + dirties[i][3] > y && dirties[i][1] + dirties[i][3] <= y + h) {
+ dirties[i][3] = y - dirties[i][1];
+ i = -1; continue;
+ }
+
+ }
+
+ // then we attempt the "lossy" combinations
+ for(int i=0; i 0 && h > 0 && cur[2] > 0 && cur[3] > 0 &&
+ ((max(x + w, cur[0] + cur[2]) - min(x, cur[0])) *
+ (max(y + h, cur[1] + cur[3]) - min(y, cur[1])) <
+ w * h + cur[2] * cur[3] + epsilon)) {
+ int a = min(cur[0], x);
+ int b = min(cur[1], y);
+ int c = max(x + w, cur[0] + cur[2]) - min(cur[0], x);
+ int d = max(y + h, cur[1] + cur[3]) - min(cur[1], y);
+ dirties[i][2] = 0;
+ dirties[i][3] = 0;
+ return dirty(a, b, c, d);
+ }
+ }
+
+ dirties[numdirties++] = new int[] { x, y, w, h };
+ return true;
+ }
+
+ /** Returns true if there are no regions that need repainting */
+ public boolean empty() { return (numdirties == 0); }
+
+ /**
+ * Atomically returns the list of dirty rectangles as an array of
+ * four-int arrays and clears the internal dirty-rectangle
+ * list. Note that some of the regions returned may be null, or
+ * may have zero height or zero width, and do not need to be
+ * repainted.
+ */
+ public synchronized int[][] flush() {
+ if (numdirties == 0) return null;
+ int[][] ret = dirties;
+ for(int i=numdirties; ib) return a;
+ else return b;
+ }
+
+ /** included here so that it can be inlined */
+ private static final int min(int a, int b, int c) {
+ if (a<=b && a<=c) return a;
+ else if (b<=c && b<=a) return b;
+ else return c;
+ }
+
+ /** included here so that it can be inlined */
+ private static final int max(int a, int b, int c) {
+ if (a>=b && a>=c) return a;
+ else if (b>=c && b>=a) return b;
+ else return c;
+ }
+
+ /** included here so that it can be inlined */
+ private static final int bound(int a, int b, int c) {
+ if (a > b) return a;
+ if (c < b) return c;
+ return b;
+ }
+
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/EjAlbertBrowserLauncher.java b/upstream/org.ibex.core/build/java/org/ibex/util/EjAlbertBrowserLauncher.java
new file mode 100644
index 0000000..cf46138
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/EjAlbertBrowserLauncher.java
@@ -0,0 +1,589 @@
+package org.ibex.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * BrowserLauncher is a class that provides one static method, openURL, which opens the default
+ * web browser for the current user of the system to the given URL. It may support other
+ * protocols depending on the system -- mailto, ftp, etc. -- but that has not been rigorously
+ * tested and is not guaranteed to work.
+ *
+ * Yes, this is platform-specific code, and yes, it may rely on classes on certain platforms
+ * that are not part of the standard JDK. What we're trying to do, though, is to take something
+ * that's frequently desirable but inherently platform-specific -- opening a default browser --
+ * and allow programmers (you, for example) to do so without worrying about dropping into native
+ * code or doing anything else similarly evil.
+ *
+ * Anyway, this code is completely in Java and will run on all JDK 1.1-compliant systems without
+ * modification or a need for additional libraries. All classes that are required on certain
+ * platforms to allow this to run are dynamically loaded at runtime via reflection and, if not
+ * found, will not cause this to do anything other than returning an error when opening the
+ * browser.
+ *
+ * There are certain system requirements for this class, as it's running through Runtime.exec(),
+ * which is Java's way of making a native system call. Currently, this requires that a Macintosh
+ * have a Finder which supports the GURL event, which is true for Mac OS 8.0 and 8.1 systems that
+ * have the Internet Scripting AppleScript dictionary installed in the Scripting Additions folder
+ * in the Extensions folder (which is installed by default as far as I know under Mac OS 8.0 and
+ * 8.1), and for all Mac OS 8.5 and later systems. On Windows, it only runs under Win32 systems
+ * (Windows 95, 98, and NT 4.0, as well as later versions of all). On other systems, this drops
+ * back from the inherently platform-sensitive concept of a default browser and simply attempts
+ * to launch Netscape via a shell command.
+ *
+ * This code is Copyright 1999-2001 by Eric Albert (ejalbert@cs.stanford.edu) and may be
+ * redistributed or modified in any form without restrictions as long as the portion of this
+ * comment from this paragraph through the end of the comment is not removed. The author
+ * requests that he be notified of any application, applet, or other binary that makes use of
+ * this code, but that's more out of curiosity than anything and is not required. This software
+ * includes no warranty. The author is not repsonsible for any loss of data or functionality
+ * or any adverse or unexpected effects of using this software.
+ *
+ * Credits:
+ * Steven Spencer, JavaWorld magazine (Java Tip 66)
+ * Thanks also to Ron B. Yeh, Eric Shapiro, Ben Engber, Paul Teitlebaum, Andrea Cantatore,
+ * Larry Barowski, Trevor Bedzek, Frank Miedrich, and Ron Rabakukk
+ *
+ * @author Eric Albert (ejalbert@cs.stanford.edu)
+ * @version 1.4b1 (Released June 20, 2001)
+ */
+public class EjAlbertBrowserLauncher {
+
+ /**
+ * The Java virtual machine that we are running on. Actually, in most cases we only care
+ * about the operating system, but some operating systems require us to switch on the VM. */
+ private static int jvm;
+
+ /** The browser for the system */
+ private static Object browser;
+
+ /**
+ * Caches whether any classes, methods, and fields that are not part of the JDK and need to
+ * be dynamically loaded at runtime loaded successfully.
+ *
+ * Note that if this is false, openURL() will always return an
+ * IOException.
+ */
+ private static boolean loadedWithoutErrors;
+
+ /** The com.apple.mrj.MRJFileUtils class */
+ private static Class mrjFileUtilsClass;
+
+ /** The com.apple.mrj.MRJOSType class */
+ private static Class mrjOSTypeClass;
+
+ /** The com.apple.MacOS.AEDesc class */
+ private static Class aeDescClass;
+
+ /** The (int) method of com.apple.MacOS.AETarget */
+ private static Constructor aeTargetConstructor;
+
+ /** The (int, int, int) method of com.apple.MacOS.AppleEvent */
+ private static Constructor appleEventConstructor;
+
+ /** The (String) method of com.apple.MacOS.AEDesc */
+ private static Constructor aeDescConstructor;
+
+ /** The findFolder method of com.apple.mrj.MRJFileUtils */
+ private static Method findFolder;
+
+ /** The getFileCreator method of com.apple.mrj.MRJFileUtils */
+ private static Method getFileCreator;
+
+ /** The getFileType method of com.apple.mrj.MRJFileUtils */
+ private static Method getFileType;
+
+ /** The openURL method of com.apple.mrj.MRJFileUtils */
+ private static Method openURLm;
+
+ /** The makeOSType method of com.apple.MacOS.OSUtils */
+ private static Method makeOSType;
+
+ /** The putParameter method of com.apple.MacOS.AppleEvent */
+ private static Method putParameter;
+
+ /** The sendNoReply method of com.apple.MacOS.AppleEvent */
+ private static Method sendNoReply;
+
+ /** Actually an MRJOSType pointing to the System Folder on a Macintosh */
+ private static Object kSystemFolderType;
+
+ /** The keyDirectObject AppleEvent parameter type */
+ private static Integer keyDirectObject;
+
+ /** The kAutoGenerateReturnID AppleEvent code */
+ private static Integer kAutoGenerateReturnID;
+
+ /** The kAnyTransactionID AppleEvent code */
+ private static Integer kAnyTransactionID;
+
+ /** The linkage object required for JDirect 3 on Mac OS X. */
+ private static Object linkage;
+
+ /** The framework to reference on Mac OS X */
+ private static final String JDirect_MacOSX = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox";
+
+ /** JVM constant for MRJ 2.0 */
+ private static final int MRJ_2_0 = 0;
+
+ /** JVM constant for MRJ 2.1 or later */
+ private static final int MRJ_2_1 = 1;
+
+ /** JVM constant for Java on Mac OS X 10.0 (MRJ 3.0) */
+ private static final int MRJ_3_0 = 3;
+
+ /** JVM constant for MRJ 3.1 */
+ private static final int MRJ_3_1 = 4;
+
+ /** JVM constant for any Windows NT JVM */
+ private static final int WINDOWS_NT = 5;
+
+ /** JVM constant for any Windows 9x JVM */
+ private static final int WINDOWS_9x = 6;
+
+ /** JVM constant for any other platform */
+ private static final int OTHER = -1;
+
+ /**
+ * The file type of the Finder on a Macintosh. Hardcoding "Finder" would keep non-U.S. English
+ * systems from working properly.
+ */
+ private static final String FINDER_TYPE = "FNDR";
+
+ /**
+ * The creator code of the Finder on a Macintosh, which is needed to send AppleEvents to the
+ * application.
+ */
+ private static final String FINDER_CREATOR = "MACS";
+
+ /** The name for the AppleEvent type corresponding to a GetURL event. */
+ private static final String GURL_EVENT = "GURL";
+
+ /**
+ * The first parameter that needs to be passed into Runtime.exec() to open the default web
+ * browser on Windows.
+ */
+ private static final String FIRST_WINDOWS_PARAMETER = "/c";
+
+ /** The second parameter for Runtime.exec() on Windows. */
+ private static final String SECOND_WINDOWS_PARAMETER = "start";
+
+ /**
+ * The third parameter for Runtime.exec() on Windows. This is a "title"
+ * parameter that the command line expects. Setting this parameter allows
+ * URLs containing spaces to work.
+ */
+ private static final String THIRD_WINDOWS_PARAMETER = "\"\"";
+
+ /**
+ * The shell parameters for Netscape that opens a given URL in an already-open copy of Netscape
+ * on many command-line systems.
+ */
+ private static final String NETSCAPE_REMOTE_PARAMETER = "-remote";
+ private static final String NETSCAPE_OPEN_PARAMETER_START = "'openURL(";
+ private static final String NETSCAPE_OPEN_PARAMETER_END = ")'";
+
+ /**
+ * The message from any exception thrown throughout the initialization process.
+ */
+ private static String errorMessage;
+
+ /**
+ * An initialization block that determines the operating system and loads the necessary
+ * runtime data.
+ */
+ static {
+ loadedWithoutErrors = true;
+ String osName = System.getProperty("os.name");
+ if (osName.startsWith("Mac OS")) {
+ String mrjVersion = System.getProperty("mrj.version");
+ String majorMRJVersion = mrjVersion.substring(0, 3);
+ try {
+ double version = Double.valueOf(majorMRJVersion).doubleValue();
+ if (version == 2) {
+ jvm = MRJ_2_0;
+ } else if (version >= 2.1 && version < 3) {
+ // Assume that all 2.x versions of MRJ work the same. MRJ 2.1 actually
+ // works via Runtime.exec() and 2.2 supports that but has an openURL() method
+ // as well that we currently ignore.
+ jvm = MRJ_2_1;
+ } else if (version == 3.0) {
+ jvm = MRJ_3_0;
+ } else if (version >= 3.1) {
+ // Assume that all 3.1 and later versions of MRJ work the same.
+ jvm = MRJ_3_1;
+ } else {
+ loadedWithoutErrors = false;
+ errorMessage = "Unsupported MRJ version: " + version;
+ }
+ } catch (NumberFormatException nfe) {
+ loadedWithoutErrors = false;
+ errorMessage = "Invalid MRJ version: " + mrjVersion;
+ }
+ } else if (osName.startsWith("Windows")) {
+ if (osName.indexOf("9") != -1) {
+ jvm = WINDOWS_9x;
+ } else {
+ jvm = WINDOWS_NT;
+ }
+ } else {
+ jvm = OTHER;
+ }
+
+ if (loadedWithoutErrors) { // if we haven't hit any errors yet
+ loadedWithoutErrors = loadClasses();
+ }
+ }
+
+ /**
+ * This class should be never be instantiated; this just ensures so.
+ */
+ private EjAlbertBrowserLauncher() { }
+
+ /**
+ * Called by a static initializer to load any classes, fields, and methods required at runtime
+ * to locate the user's web browser.
+ * @return true if all intialization succeeded
+ * false if any portion of the initialization failed
+ */
+ private static boolean loadClasses() {
+ switch (jvm) {
+ case MRJ_2_0:
+ try {
+ Class aeTargetClass = Class.forName("com.apple.MacOS.AETarget");
+ Class osUtilsClass = Class.forName("com.apple.MacOS.OSUtils");
+ Class appleEventClass = Class.forName("com.apple.MacOS.AppleEvent");
+ Class aeClass = Class.forName("com.apple.MacOS.ae");
+ aeDescClass = Class.forName("com.apple.MacOS.AEDesc");
+
+ aeTargetConstructor = aeTargetClass.getDeclaredConstructor(new Class [] { int.class });
+ appleEventConstructor = appleEventClass.getDeclaredConstructor(new Class[] { int.class, int.class, aeTargetClass, int.class, int.class });
+ aeDescConstructor = aeDescClass.getDeclaredConstructor(new Class[] { String.class });
+
+ makeOSType = osUtilsClass.getDeclaredMethod("makeOSType", new Class [] { String.class });
+ putParameter = appleEventClass.getDeclaredMethod("putParameter", new Class[] { int.class, aeDescClass });
+ sendNoReply = appleEventClass.getDeclaredMethod("sendNoReply", new Class[] { });
+
+ Field keyDirectObjectField = aeClass.getDeclaredField("keyDirectObject");
+ keyDirectObject = (Integer) keyDirectObjectField.get(null);
+ Field autoGenerateReturnIDField = appleEventClass.getDeclaredField("kAutoGenerateReturnID");
+ kAutoGenerateReturnID = (Integer) autoGenerateReturnIDField.get(null);
+ Field anyTransactionIDField = appleEventClass.getDeclaredField("kAnyTransactionID");
+ kAnyTransactionID = (Integer) anyTransactionIDField.get(null);
+ } catch (ClassNotFoundException cnfe) {
+ errorMessage = cnfe.getMessage();
+ return false;
+ } catch (NoSuchMethodException nsme) {
+ errorMessage = nsme.getMessage();
+ return false;
+ } catch (NoSuchFieldException nsfe) {
+ errorMessage = nsfe.getMessage();
+ return false;
+ } catch (IllegalAccessException iae) {
+ errorMessage = iae.getMessage();
+ return false;
+ }
+ break;
+ case MRJ_2_1:
+ try {
+ mrjFileUtilsClass = Class.forName("com.apple.mrj.MRJFileUtils");
+ mrjOSTypeClass = Class.forName("com.apple.mrj.MRJOSType");
+ Field systemFolderField = mrjFileUtilsClass.getDeclaredField("kSystemFolderType");
+ kSystemFolderType = systemFolderField.get(null);
+ findFolder = mrjFileUtilsClass.getDeclaredMethod("findFolder", new Class[] { mrjOSTypeClass });
+ getFileCreator = mrjFileUtilsClass.getDeclaredMethod("getFileCreator", new Class[] { File.class });
+ getFileType = mrjFileUtilsClass.getDeclaredMethod("getFileType", new Class[] { File.class });
+ } catch (ClassNotFoundException cnfe) {
+ errorMessage = cnfe.getMessage();
+ return false;
+ } catch (NoSuchFieldException nsfe) {
+ errorMessage = nsfe.getMessage();
+ return false;
+ } catch (NoSuchMethodException nsme) {
+ errorMessage = nsme.getMessage();
+ return false;
+ } catch (SecurityException se) {
+ errorMessage = se.getMessage();
+ return false;
+ } catch (IllegalAccessException iae) {
+ errorMessage = iae.getMessage();
+ return false;
+ }
+ break;
+ case MRJ_3_0:
+ try {
+ Class linker = Class.forName("com.apple.mrj.jdirect.Linker");
+ Constructor constructor = linker.getConstructor(new Class[]{ Class.class });
+ linkage = constructor.newInstance(new Object[] { EjAlbertBrowserLauncher.class });
+ } catch (ClassNotFoundException cnfe) {
+ errorMessage = cnfe.getMessage();
+ return false;
+ } catch (NoSuchMethodException nsme) {
+ errorMessage = nsme.getMessage();
+ return false;
+ } catch (InvocationTargetException ite) {
+ errorMessage = ite.getMessage();
+ return false;
+ } catch (InstantiationException ie) {
+ errorMessage = ie.getMessage();
+ return false;
+ } catch (IllegalAccessException iae) {
+ errorMessage = iae.getMessage();
+ return false;
+ }
+ break;
+ case MRJ_3_1:
+ try {
+ mrjFileUtilsClass = Class.forName("com.apple.mrj.MRJFileUtils");
+ openURLm = mrjFileUtilsClass.getDeclaredMethod("openURL", new Class[] { String.class });
+ } catch (ClassNotFoundException cnfe) {
+ errorMessage = cnfe.getMessage();
+ return false;
+ } catch (NoSuchMethodException nsme) {
+ errorMessage = nsme.getMessage();
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * Attempts to locate the default web browser on the local system. Caches results so it
+ * only locates the browser once for each use of this class per JVM instance.
+ * @return The browser for the system. Note that this may not be what you would consider
+ * to be a standard web browser; instead, it's the application that gets called to
+ * open the default web browser. In some cases, this will be a non-String object
+ * that provides the means of calling the default browser.
+ */
+ private static Object locateBrowser() {
+ if (browser != null) {
+ return browser;
+ }
+ switch (jvm) {
+ case MRJ_2_0:
+ try {
+ Integer finderCreatorCode = (Integer) makeOSType.invoke(null, new Object[] { FINDER_CREATOR });
+ Object aeTarget = aeTargetConstructor.newInstance(new Object[] { finderCreatorCode });
+ Integer gurlType = (Integer) makeOSType.invoke(null, new Object[] { GURL_EVENT });
+ Object appleEvent = appleEventConstructor.newInstance(new Object[] { gurlType, gurlType, aeTarget, kAutoGenerateReturnID, kAnyTransactionID });
+ // Don't set browser = appleEvent because then the next time we call
+ // locateBrowser(), we'll get the same AppleEvent, to which we'll already have
+ // added the relevant parameter. Instead, regenerate the AppleEvent every time.
+ // There's probably a way to do this better; if any has any ideas, please let
+ // me know.
+ return appleEvent;
+ } catch (IllegalAccessException iae) {
+ browser = null;
+ errorMessage = iae.getMessage();
+ return browser;
+ } catch (InstantiationException ie) {
+ browser = null;
+ errorMessage = ie.getMessage();
+ return browser;
+ } catch (InvocationTargetException ite) {
+ browser = null;
+ errorMessage = ite.getMessage();
+ return browser;
+ }
+ case MRJ_2_1:
+ File systemFolder;
+ try {
+ systemFolder = (File) findFolder.invoke(null, new Object[] { kSystemFolderType });
+ } catch (IllegalArgumentException iare) {
+ browser = null;
+ errorMessage = iare.getMessage();
+ return browser;
+ } catch (IllegalAccessException iae) {
+ browser = null;
+ errorMessage = iae.getMessage();
+ return browser;
+ } catch (InvocationTargetException ite) {
+ browser = null;
+ errorMessage = ite.getTargetException().getClass() + ": " + ite.getTargetException().getMessage();
+ return browser;
+ }
+ String[] systemFolderFiles = systemFolder.list();
+ // Avoid a FilenameFilter because that can't be stopped mid-list
+ for(int i = 0; i < systemFolderFiles.length; i++) {
+ try {
+ File file = new File(systemFolder, systemFolderFiles[i]);
+ if (!file.isFile()) {
+ continue;
+ }
+ // We're looking for a file with a creator code of 'MACS' and
+ // a type of 'FNDR'. Only requiring the type results in non-Finder
+ // applications being picked up on certain Mac OS 9 systems,
+ // especially German ones, and sending a GURL event to those
+ // applications results in a logout under Multiple Users.
+ Object fileType = getFileType.invoke(null, new Object[] { file });
+ if (FINDER_TYPE.equals(fileType.toString())) {
+ Object fileCreator = getFileCreator.invoke(null, new Object[] { file });
+ if (FINDER_CREATOR.equals(fileCreator.toString())) {
+ browser = file.toString(); // Actually the Finder, but that's OK
+ return browser;
+ }
+ }
+ } catch (IllegalArgumentException iare) {
+ browser = browser;
+ errorMessage = iare.getMessage();
+ return null;
+ } catch (IllegalAccessException iae) {
+ browser = null;
+ errorMessage = iae.getMessage();
+ return browser;
+ } catch (InvocationTargetException ite) {
+ browser = null;
+ errorMessage = ite.getTargetException().getClass() + ": " + ite.getTargetException().getMessage();
+ return browser;
+ }
+ }
+ browser = null;
+ break;
+ case MRJ_3_0:
+ case MRJ_3_1:
+ browser = ""; // Return something non-null
+ break;
+ case WINDOWS_NT:
+ browser = "cmd.exe";
+ break;
+ case WINDOWS_9x:
+ browser = "command.com";
+ break;
+ case OTHER:
+ default:
+ browser = "netscape";
+ break;
+ }
+ return browser;
+ }
+
+ /**
+ * Attempts to open the default web browser to the given URL.
+ * @param url The URL to open
+ * @throws IOException If the web browser could not be located or does not run
+ */
+ public static void openURL(String url) throws IOException {
+ if (!loadedWithoutErrors) {
+ throw new IOException("Exception in finding browser: " + errorMessage);
+ }
+ Object browser = locateBrowser();
+ if (browser == null) {
+ throw new IOException("Unable to locate browser: " + errorMessage);
+ }
+
+ switch (jvm) {
+ case MRJ_2_0:
+ Object aeDesc = null;
+ try {
+ aeDesc = aeDescConstructor.newInstance(new Object[] { url });
+ putParameter.invoke(browser, new Object[] { keyDirectObject, aeDesc });
+ sendNoReply.invoke(browser, new Object[] { });
+ } catch (InvocationTargetException ite) {
+ throw new IOException("InvocationTargetException while creating AEDesc: " + ite.getMessage());
+ } catch (IllegalAccessException iae) {
+ throw new IOException("IllegalAccessException while building AppleEvent: " + iae.getMessage());
+ } catch (InstantiationException ie) {
+ throw new IOException("InstantiationException while creating AEDesc: " + ie.getMessage());
+ } finally {
+ aeDesc = null; // Encourage it to get disposed if it was created
+ browser = null; // Ditto
+ }
+ break;
+ case MRJ_2_1:
+ Runtime.getRuntime().exec(new String[] { (String) browser, url } );
+ break;
+ case MRJ_3_0:
+ int[] instance = new int[1];
+ int result = ICStart(instance, 0);
+ if (result == 0) {
+ int[] selectionStart = new int[] { 0 };
+ byte[] urlBytes = url.getBytes();
+ int[] selectionEnd = new int[] { urlBytes.length };
+ result = ICLaunchURL(instance[0], new byte[] { 0 }, urlBytes,
+ urlBytes.length, selectionStart,
+ selectionEnd);
+ if (result == 0) {
+ // Ignore the return value; the URL was launched successfully
+ // regardless of what happens here.
+ ICStop(instance);
+ } else {
+ throw new IOException("Unable to launch URL: " + result);
+ }
+ } else {
+ throw new IOException("Unable to create an Internet Config instance: " + result);
+ }
+ break;
+ case MRJ_3_1:
+ try {
+ openURLm.invoke(null, new Object[] { url });
+ } catch (InvocationTargetException ite) {
+ throw new IOException("InvocationTargetException while calling openURL: " + ite.getMessage());
+ } catch (IllegalAccessException iae) {
+ throw new IOException("IllegalAccessException while calling openURL: " + iae.getMessage());
+ }
+ break;
+ case WINDOWS_NT:
+ case WINDOWS_9x:
+ // Add quotes around the URL to allow ampersands and other special
+ // characters to work.
+ Process process = Runtime.getRuntime().exec(new String[] { (String) browser,
+ FIRST_WINDOWS_PARAMETER,
+ SECOND_WINDOWS_PARAMETER,
+ THIRD_WINDOWS_PARAMETER,
+ '"' + url + '"' });
+ // This avoids a memory leak on some versions of Java on Windows.
+ // That's hinted at in .
+ try {
+ process.waitFor();
+ process.exitValue();
+ } catch (InterruptedException ie) {
+ throw new IOException("InterruptedException while launching browser: " + ie.getMessage());
+ }
+ break;
+ case OTHER:
+ // Assume that we're on Unix and that Netscape is installed
+
+ // First, attempt to open the URL in a currently running session of Netscape
+ process = Runtime.getRuntime().exec(new String[] { (String) browser,
+ NETSCAPE_REMOTE_PARAMETER,
+ NETSCAPE_OPEN_PARAMETER_START +
+ url +
+ NETSCAPE_OPEN_PARAMETER_END });
+ try {
+ int exitCode = process.waitFor();
+ if (exitCode != 0) { // if Netscape was not open
+ Runtime.getRuntime().exec(new String[] { (String) browser, url });
+ }
+ } catch (InterruptedException ie) {
+ throw new IOException("InterruptedException while launching browser: " + ie.getMessage());
+ }
+ break;
+ default:
+ // This should never occur, but if it does, we'll try the simplest thing possible
+ Runtime.getRuntime().exec(new String[] { (String) browser, url });
+ break;
+ }
+ }
+
+ /**
+ * Methods required for Mac OS X. The presence of native methods does not cause
+ * any problems on other platforms.
+ */
+ /*
+ private native static int ICStart(int[] instance, int signature);
+ private native static int ICStop(int[] instance);
+ private native static int ICLaunchURL(int instance, byte[] hint, byte[] data, int len,
+ int[] selectionStart, int[] selectionEnd);
+ */
+ private static int ICStart(int[] instance, int signature) { return 0; }
+ private static int ICStop(int[] instance) { return 0; }
+ private static int ICLaunchURL(int instance, byte[] hint, byte[] data, int len,
+ int[] selectionStart, int[] selectionEnd) { return 0; }
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/Grammar.java b/upstream/org.ibex.core/build/java/org/ibex/util/Grammar.java
new file mode 100644
index 0000000..565de4d
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/Grammar.java
@@ -0,0 +1,105 @@
+package org.ibex.util;
+
+import org.ibex.js.*;
+
+public abstract class Grammar extends JS {
+
+ public JS action = null;
+
+ // means we call()ed a Grammar that hasn't been bound to a scope yet
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ throw new Error("this should never happen");
+ }
+
+ private static Object NULL = new Object();
+
+ public abstract int match(String s, int start, Hash v, JSScope scope) throws JSExn;
+ public int matchAndWrite(final String s, final int start, Hash v, JSScope scope, String key) throws JSExn {
+ final Hash v2 = new Hash();
+ final int ret = match(s, start, v2, scope);
+ Object result = ret == -1 ? NULL : action == null ?
+ s.substring(start, ret) :
+ JS.cloneWithNewParentScope(action, new JSScope(scope) {
+ public Object get(Object key) throws JSExn {
+ Object val = v2.get(key);
+ if (val == NULL) return null;
+ if (val != null) return val;
+ if (key.equals("whole")) return s.substring(start, ret);
+ return super.get(key);
+ }
+ }).call(null, null, null, null, 0);
+ if (key != null) {
+ Object old = v.get(key);
+ if (old == null || old == NULL) { }
+ else if (old instanceof JSArray) { if (result != NULL) { ((JSArray)old).addElement(result); result = old; } }
+ else if (result != NULL) { JSArray j = new JSArray(); j.addElement(old); j.addElement(result); result = j; }
+ v.put(key, result);
+ }
+ return ret;
+ }
+
+ public static class Alternative extends Grammar {
+ private Grammar r1, r2;
+ public Alternative(Grammar r1, Grammar r2) { this.r1 = r1; this.r2 = r2; }
+ public int match(String s, int start, Hash v, JSScope r) throws JSExn {
+ int s1 = r1.match(s, start, v, r);
+ if (s1 != -1) return s1;
+ int s2 = r2.match(s, start, v, r);
+ if (s2 != -1) return s2;
+ return -1;
+ }
+ }
+
+ public static class Juxtaposition extends Grammar {
+ private Grammar r1, r2;
+ public Juxtaposition(Grammar r1, Grammar r2) { this.r1 = r1; this.r2 = r2; }
+ public int match(String s, int start, Hash v, JSScope r) throws JSExn {
+ int s1 = r1.match(s, start, v, r);
+ if (s1 == -1) return -1;
+ int s2 = r2.match(s, s1, v, r);
+ if (s2 == -1) return -1;
+ return s2;
+ }
+ }
+
+ public static class Repetition extends Grammar {
+ private Grammar r1;
+ private int min, max;
+ public Repetition(Grammar r1, int min, int max) { this.r1 = r1; this.min = min; this.max = max; }
+ public int match(String s, int start, Hash v, JSScope r) throws JSExn {
+ int i;
+ for(i=0; i= min && s.charAt(start) <= max)) return -1;
+ return start + 1;
+ }
+ }
+
+ public static class Reference extends Grammar {
+ String key;
+ public Reference(String key) { this.key = key; }
+ public int match(String s, int start, Hash v, JSScope scope) throws JSExn {
+ return ((Grammar)scope.get(key)).matchAndWrite(s, start, v, scope, key);
+ }
+ }
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/Hash.java b/upstream/org.ibex.core/build/java/org/ibex/util/Hash.java
new file mode 100644
index 0000000..4b38870
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/Hash.java
@@ -0,0 +1,174 @@
+// Copyright (C) 2003 Adam Megacz all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.ibex.util;
+
+import java.util.*;
+
+/** Implementation of an unsynchronized hash table, with one or two
+ * keys, using Radke's quadradic residue linear probing instead of
+ * buckets to minimize object count (less allocations, faster GC).
+ * See C. Radke, Communications of the ACM, 1970, 103-105
+ *
+ * Not threadsafe.
+ */
+public class Hash {
+ /** this object is inserted as key in a slot when the
+ * corresponding value is removed -- this ensures that the
+ * probing sequence for any given key remains the same even if
+ * other keys are removed.
+ */
+ private static Object placeholder = new Object();
+
+ /** the number of entries with at least one non-null key */
+ private int usedslots = 0;
+
+ /** the number of entries with non-null values */
+ protected int size = 0;
+
+ /** when num_slots < loadFactor * size, rehash into a bigger table */
+ private final int loadFactor;
+
+ /** primary keys */
+ private Object[] keys1 = null;
+
+ /** secondary keys; null if no secondary key has ever been added */
+ private Object[] keys2 = null;
+
+ /** the values for the table */
+ private Object[] vals = null;
+
+ /** the number of entries with a non-null value */
+ public int size() { return size; }
+
+ /** empties the table */
+ public void clear() {
+ size = 0;
+ usedslots = 0;
+ for(int i=0; i vals.length) rehash();
+ int hash = (k1 == null ? 0 : k1.hashCode()) ^ (k2 == null ? 0 : k2.hashCode());
+ int dest = Math.abs(hash) % vals.length;
+ int odest = dest;
+ boolean plus = true;
+ int tries = 1;
+ while (true) {
+ Object hk1 = keys1[dest];
+ Object hk2 = keys2 == null ? null : keys2[dest];
+ if (hk1 == null && hk2 == null) { // empty slot
+ if (v == null) return;
+ size++;
+ usedslots++;
+ break;
+ }
+
+ if ((k1 == hk1 || (k1 != null && hk1 != null && k1.equals(hk1))) && // replacing former entry
+ (k2 == hk2 || (k2 != null && hk2 != null && k2.equals(hk2)))) {
+
+ // we don't actually remove things from the table; rather, we insert a placeholder
+ if (v == null) {
+ k1 = placeholder;
+ k2 = null;
+ size--;
+ }
+ break;
+ }
+
+ dest = Math.abs((odest + (plus ? 1 : -1 ) * tries * tries) % vals.length);
+ if (plus) tries++;
+ plus = !plus;
+ }
+
+ keys1[dest] = k1;
+ if (k2 != null && keys2 == null) keys2 = new Object[keys1.length];
+ if (keys2 != null) keys2[dest] = k2;
+ vals[dest] = v;
+ }
+
+ private class HashEnum implements java.util.Enumeration {
+ private int iterator = 0;
+ private int found = 0;
+
+ public boolean hasMoreElements() {
+ return found < usedslots;
+ }
+
+ public Object nextElement() {
+ if (!hasMoreElements()) throw new java.util.NoSuchElementException();
+
+ Object o = null;
+ while (o == null) o = keys1[iterator++];
+ if (o == null) throw new IllegalStateException("Didn't find an element, when I should have.");
+ found++;
+
+ return o;
+ }
+ }
+}
+
+
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/InputStreamToByteArray.java b/upstream/org.ibex.core/build/java/org/ibex/util/InputStreamToByteArray.java
new file mode 100644
index 0000000..7e19644
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/InputStreamToByteArray.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2003 Adam Megacz all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.ibex.util;
+import java.io.*;
+
+public class InputStreamToByteArray {
+
+ /** scratch space for isToByteArray() */
+ private static byte[] workspace = new byte[16 * 1024];
+
+ /** Trivial method to completely read an InputStream */
+ public static synchronized byte[] convert(InputStream is) throws IOException {
+ int pos = 0;
+ while (true) {
+ int numread = is.read(workspace, pos, workspace.length - pos);
+ if (numread == -1) break;
+ else if (pos + numread < workspace.length) pos += numread;
+ else {
+ pos += numread;
+ byte[] temp = new byte[workspace.length * 2];
+ System.arraycopy(workspace, 0, temp, 0, workspace.length);
+ workspace = temp;
+ }
+ }
+ byte[] ret = new byte[pos];
+ System.arraycopy(workspace, 0, ret, 0, pos);
+ return ret;
+ }
+
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/KnownLength.java b/upstream/org.ibex.core/build/java/org/ibex/util/KnownLength.java
new file mode 100644
index 0000000..06118bb
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/KnownLength.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2003 Adam Megacz all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.ibex.util;
+import java.io.*;
+
+/** a generic interface for things that "know" their length */
+public interface KnownLength {
+
+ public abstract int getLength();
+
+ public static class KnownLengthInputStream extends FilterInputStream implements KnownLength {
+ int length;
+ public int getLength() { return length; }
+ public KnownLengthInputStream(java.io.InputStream parent, int length) {
+ super(parent);
+ this.length = length;
+ }
+ }
+
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/LineReader.java b/upstream/org.ibex.core/build/java/org/ibex/util/LineReader.java
new file mode 100644
index 0000000..972dfe5
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/LineReader.java
@@ -0,0 +1,40 @@
+package org.ibex.util;
+import java.io.*;
+
+public class LineReader {
+
+ private static int MAXBUF = 1024 * 16;
+ char[] buf = new char[MAXBUF];
+ int buflen = 0;
+ Reader r;
+ Vec pushback = new Vec();
+
+ public LineReader(Reader r) { this.r = r; }
+
+ public void pushback(String s) { pushback.push(s); }
+
+ public String readLine() throws IOException {
+ while(true) {
+ if (pushback.size() > 0) return (String)pushback.pop();
+ for(int i=0; i all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.ibex.util;
+import org.ibex.js.*;
+import java.io.*;
+import java.util.*;
+import java.net.*;
+
+/** easy to use logger */
+public class Log {
+
+ public static boolean on = true;
+ public static boolean rpc = false;
+ public static boolean color = false;
+ public static boolean verbose = false;
+ public static boolean logDates = false;
+ public static Date lastDate = null;
+
+ public static PrintStream logstream = System.err;
+
+ public static void email(String address) { throw new Error("FIXME not supported"); }
+ public static void file(String filename) throws IOException {
+ // FIXME security
+ logstream = new PrintStream(new FileOutputStream(filename));
+ }
+ public static void tcp(String host, int port) throws IOException {
+ // FIXME security
+ logstream = new PrintStream(new Socket(InetAddress.getByName(host), port).getOutputStream());
+ }
+
+ private static Hashtable threadAnnotations = new Hashtable();
+ public static void setThreadAnnotation(String s) { threadAnnotations.put(Thread.currentThread(), s); }
+
+ /** true iff nothing has yet been logged */
+ public static boolean firstMessage = true;
+
+ /** message can be a String or a Throwable */
+ public static synchronized void echo(Object o, Object message) { log(o, message, ECHO); }
+ public static synchronized void diag(Object o, Object message) { log(o, message, DIAGNOSTIC); }
+ public static synchronized void debug(Object o, Object message) { log(o, message, DEBUG); }
+ public static synchronized void info(Object o, Object message) { log(o, message, INFO); }
+ public static synchronized void warn(Object o, Object message) { log(o, message, WARN); }
+ public static synchronized void error(Object o, Object message) { log(o, message, ERROR); }
+
+ // these two logging levels serve ONLY to change the color; semantically they are the same as DEBUG
+ private static final int DIAGNOSTIC = -2;
+ private static final int ECHO = -1;
+
+ // the usual log4j levels, minus FAIL (we just throw an Error in that case)
+ public static final int DEBUG = 0;
+ public static final int INFO = 1;
+ public static final int WARN = 2;
+ public static final int ERROR = 3;
+ public static final int SILENT = Integer.MAX_VALUE;
+ public static int level = INFO;
+
+ private static final int BLUE = 34;
+ private static final int GREEN = 32;
+ private static final int CYAN = 36;
+ private static final int RED = 31;
+ private static final int PURPLE = 35;
+ private static final int BROWN = 33;
+ private static final int GRAY = 37;
+
+ private static String colorize(int color, boolean bright, String s) {
+ if (!Log.color) return s;
+ return
+ "\033[40;" + (bright?"1;":"") + color + "m" +
+ s +
+ "\033[0m";
+ }
+
+ private static String lastClassName = null;
+ private static synchronized void log(Object o, Object message, int level) {
+ if (level < Log.level) return;
+ if (firstMessage && !logDates) {
+ firstMessage = false;
+ logstream.println(colorize(GREEN, false, "==========================================================================="));
+
+ // FIXME later: causes problems with method pruning
+ //diag(Log.class, "Logging enabled at " + new java.util.Date());
+
+ if (color) diag(Log.class, "logging messages in " +
+ colorize(BLUE, true, "c") +
+ colorize(RED, true, "o") +
+ colorize(CYAN, true, "l") +
+ colorize(GREEN, true, "o") +
+ colorize(PURPLE, true, "r"));
+ }
+
+ String classname;
+ if (o instanceof Class) {
+ classname = ((Class)o).getName();
+ if (classname.indexOf('.') != -1) classname = classname.substring(classname.lastIndexOf('.') + 1);
+ }
+ else if (o instanceof String) classname = (String)o;
+ else classname = o.getClass().getName();
+
+ if (classname.equals(lastClassName)) classname = "";
+ else lastClassName = classname;
+
+ if (classname.length() > (logDates ? 14 : 20)) classname = classname.substring(0, (logDates ? 14 : 20));
+ while (classname.length() < (logDates ? 14 : 20)) classname = " " + classname;
+ classname = classname + (classname.trim().length() == 0 ? " " : ": ");
+ classname = colorize(GRAY, true, classname);
+ classname = classname.replace('$', '.');
+
+ if (logDates) {
+ Date d = new Date();
+ if (lastDate == null || d.getYear() != lastDate.getYear() || d.getMonth() != lastDate.getMonth() || d.getDay() != lastDate.getDay()) {
+ String now = new java.text.SimpleDateFormat("EEE dd MMM yyyy").format(d);
+ logstream.println();
+ logstream.println(colorize(GRAY, false, "=== " + now + " =========================================================="));
+ }
+ java.text.DateFormat df = new java.text.SimpleDateFormat("[EEE HH:mm:ss] ");
+ classname = df.format(d) + classname;
+ lastDate = d;
+ }
+
+ String annot = (String)threadAnnotations.get(Thread.currentThread());
+ if (annot != null) classname += annot;
+
+ if (message instanceof Throwable) {
+ if (level < ERROR) level = WARN;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ((Throwable)message).printStackTrace(new PrintStream(baos));
+ byte[] b = baos.toByteArray();
+ BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(b)));
+ String s = null;
+ try {
+ String m = "";
+ while((s = br.readLine()) != null) m += s + "\n";
+ if (m.length() > 0) log(o, m.substring(0, m.length() - 1), level);
+ } catch (IOException e) {
+ logstream.println(colorize(RED, true, "Logger: exception thrown by ByteArrayInputStream -- this should not happen"));
+ }
+ lastClassName = "";
+ return;
+ }
+
+ String str = message.toString();
+ if (str.indexOf('\n') != -1) lastClassName = "";
+ while(str.indexOf('\t') != -1)
+ str = str.substring(0, str.indexOf('\t')) + " " + str.substring(str.indexOf('\t') + 1);
+
+ classname = colorize(GRAY, false, classname);
+ int levelcolor = GRAY;
+ boolean bright = true;
+ switch (level) {
+ case DIAGNOSTIC: levelcolor = GREEN; bright = false; break;
+ case ECHO: levelcolor = BLUE; bright = true; break;
+ case DEBUG: levelcolor = BROWN; bright = true; break;
+ case INFO: levelcolor = GRAY; bright = false; break;
+ case WARN: levelcolor = BROWN; bright = false; break;
+ case ERROR: levelcolor = RED; bright = true; break;
+ }
+
+ while(str.indexOf('\n') != -1) {
+ logstream.println(classname + colorize(levelcolor, bright, str.substring(0, str.indexOf('\n'))));
+ classname = logDates ? " " : " ";
+ classname = colorize(GRAY,false,classname);
+ str = str.substring(str.indexOf('\n') + 1);
+ }
+ logstream.println(classname + colorize(levelcolor, bright, str));
+ }
+
+ public static void recursiveLog(String indent, String name, Object o) throws JSExn {
+ if (!name.equals("")) name += " : ";
+
+ if (o == null) {
+ JS.log(indent + name + "");
+
+ } else if (o instanceof JSArray) {
+ JS.log(indent + name + "");
+ JSArray na = (JSArray)o;
+ for(int i=0; i");
+ JS s = (JS)o;
+ Enumeration e = s.keys();
+ while(e.hasMoreElements()) {
+ Object key = e.nextElement();
+ if (key != null)
+ recursiveLog(indent + " ", key.toString(),
+ (key instanceof Integer) ?
+ s.get(((Integer)key)) : s.get(key.toString()));
+ }
+ } else {
+ JS.log(indent + name + o);
+
+ }
+ }
+
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/MSPack.java b/upstream/org.ibex.core/build/java/org/ibex/util/MSPack.java
new file mode 100644
index 0000000..625ffec
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/MSPack.java
@@ -0,0 +1,91 @@
+package org.ibex.util;
+
+import org.ibex.core.Main;
+import org.ibex.util.*;
+import org.xwt.mips.*;
+import java.io.*;
+
+import org.xwt.mips.Runtime;
+
+public class MSPack {
+ private static byte[] image;
+
+ private String[] fileNames;
+ private int[] lengths;
+ private byte[][] data;
+
+ public static class MSPackException extends IOException { public MSPackException(String s) { super(s); } }
+
+ public MSPack(InputStream cabIS) throws IOException {
+ try {
+ Runtime vm = (Runtime)Class.forName("org.ibex.util.MIPSApps").newInstance();
+ byte[] cab = InputStreamToByteArray.convert(cabIS);
+ int cabAddr = vm.sbrk(cab.length);
+ if(cabAddr < 0) throw new MSPackException("sbrk failed");
+
+ vm.copyout(cab,cabAddr,cab.length);
+
+ vm.setUserInfo(0,cabAddr);
+ vm.setUserInfo(1,cab.length);
+
+ int status = vm.run(new String[]{ "mspack"} );
+ if(status != 0) throw new MSPackException("mspack.mips failed (" + status + ")");
+
+ /*static struct {
+ char *filename;
+ char *data;
+ int length;
+ } output_table[MAX_MEMBERS+1]; */
+
+ int filesTable = vm.getUserInfo(2);
+ int count=0;
+ while(vm.memRead(filesTable+count*12) != 0) count++;
+
+ fileNames = new String[count];
+ data = new byte[count][];
+ lengths = new int[count];
+
+ for(int i=0,addr=filesTable;i all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.ibex.util;
+
+/** packs 8-bit bytes into a String of 7-bit chars (to avoid the UTF-8 non-ASCII penalty) */
+public class PackBytesIntoString {
+
+ public static String pack(byte[] b, int off, int len) throws IllegalArgumentException {
+ if (len % 7 != 0) throw new IllegalArgumentException("len must be a multiple of 7");
+ StringBuffer ret = new StringBuffer();
+ for(int i=off; i=0; j--) {
+ l <<= 8;
+ l |= (b[i + j] & 0xff);
+ }
+ for(int j=0; j<8; j++) {
+ ret.append((char)(l & 0x7f));
+ l >>= 7;
+ }
+ }
+ return ret.toString();
+ }
+
+ public static byte[] unpack(String s) throws IllegalArgumentException {
+ if (s.length() % 8 != 0) throw new IllegalArgumentException("string length must be a multiple of 8");
+ byte[] ret = new byte[(s.length() / 8) * 7];
+ for(int i=0; i=0; j--) {
+ l <<= 7;
+ l |= (s.charAt(i + j) & 0x7fL);
+ }
+ for(int j=0; j<7; j++) {
+ ret[(i / 8) * 7 + j] = (byte)(l & 0xff);
+ l >>= 8;
+ }
+ }
+ return ret;
+ }
+}
+
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/Queue.java b/upstream/org.ibex.core/build/java/org/ibex/util/Queue.java
new file mode 100644
index 0000000..91b9b29
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/Queue.java
@@ -0,0 +1,91 @@
+// Copyright (C) 2003 Adam Megacz all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.ibex.util;
+
+/** A simple synchronized queue, implemented as an array */
+public class Queue {
+
+ public Queue(int initiallength) { vec = new Object[initiallength]; }
+
+ /** The store */
+ private Object[] vec;
+
+ /** The index of the first node in the queue */
+ private int first = 0;
+
+ /** The number of elements in the queue; INVARAINT: size <= vec.length */
+ private int size = 0;
+
+ /** Grow the queue, if needed */
+ private void grow(int newlength) {
+ Object[] newvec = new Object[newlength];
+ if (first + size > vec.length) {
+ System.arraycopy(vec, first, newvec, 0, vec.length - first);
+ System.arraycopy(vec, 0, newvec, vec.length - first, size - (vec.length - first));
+ } else {
+ System.arraycopy(vec, first, newvec, 0, size);
+ }
+ first = 0;
+ vec = newvec;
+ }
+
+ /** The number of elements in the queue */
+ public int size() { return size; }
+
+ /** Empties the queue */
+ public synchronized void flush() {
+ first = 0;
+ size = 0;
+ for(int i=0; i= vec.length) vec[first + size - vec.length] = o;
+ else vec[first + size] = o;
+ size++;
+ if (size == 1) notify();
+ }
+
+ /** Remove and return and element from the queue, blocking if empty. */
+ public Object remove() { return remove(true); }
+
+ /** Remove and return an element from the queue, blocking if
+ block is true and the queue is empty. */
+ public synchronized Object remove(boolean block) {
+
+ while (size == 0 && block) {
+ try { wait(); } catch (InterruptedException e) { }
+ }
+
+ if (!block && size == 0) return null;
+ Object ret = vec[first];
+ first++;
+ size--;
+ if (first >= vec.length) first = 0;
+ return ret;
+ }
+
+ /** Returns the top element in the queue without removing it */
+ public synchronized Object peek() {
+ if (size == 0) return null;
+ return vec[first];
+ }
+
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/Scheduler.java b/upstream/org.ibex.core/build/java/org/ibex/util/Scheduler.java
new file mode 100644
index 0000000..e4c85fa
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/Scheduler.java
@@ -0,0 +1,94 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.util;
+
+import java.io.IOException;
+
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.graphics.*;
+import org.ibex.plat.*;
+
+/** Implements cooperative multitasking */
+public class Scheduler {
+
+ // Public API Exposed to org.ibex /////////////////////////////////////////////////
+
+ private static Scheduler singleton;
+ public static void add(Task t) { Log.debug(Scheduler.class, "scheduling " + t); Scheduler.runnable.append(t); }
+ public static void init() { if (singleton == null) (singleton = Platform.getScheduler()).run(); }
+
+ private static Task current = null;
+
+ private static volatile boolean rendering = false;
+ private static volatile boolean again = false;
+
+ /** synchronizd so that we can safely call it from an event-delivery thread, in-context */
+ public static void renderAll() {
+ if (rendering) { again = true; return; }
+ synchronized(Scheduler.class) {
+ try {
+ rendering = true;
+ do {
+ // FEATURE: this could be cleaner
+ again = false;
+ for(int i=0; i all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.ibex.util;
+
+/** Simple implementation of a blocking, counting semaphore. */
+public class Semaphore {
+
+ private int val = 0;
+
+ /** Decrement the counter, blocking if zero. */
+ public synchronized void block() {
+ while(val == 0) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ } catch (Throwable e) {
+ if (Log.on) Log.info(this, "Exception in Semaphore.block(); this should never happen");
+ if (Log.on) Log.info(this, e);
+ }
+ }
+ val--;
+ }
+
+ /** Incremenet the counter. */
+ public synchronized void release() {
+ val++;
+ notify();
+ }
+
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/Simplex.java b/upstream/org.ibex.core/build/java/org/ibex/util/Simplex.java
new file mode 100644
index 0000000..2ae314a
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/Simplex.java
@@ -0,0 +1,1345 @@
+package org.ibex.util;
+import java.io.* ;
+import java.util.* ;
+
+public class Simplex {
+
+ public final static short FAIL = -1;
+
+ public final static short NULL = 0;
+ public final static short FALSE = 0;
+ public final static short TRUE = 1;
+
+ public final static short DEFNUMINV = 50;
+
+ /* solve status values */
+ public final static short OPTIMAL = 0;
+ public final static short MILP_FAIL = 1;
+ public final static short INFEASIBLE = 2;
+ public final static short UNBOUNDED = 3;
+ public final static short FAILURE = 4;
+ public final static short RUNNING = 5;
+
+ /* lag_solve extra status values */
+ public final static short FEAS_FOUND = 6;
+ public final static short NO_FEAS_FOUND = 7;
+ public final static short BREAK_BB = 8;
+
+ public final static short FIRST_NI = 0;
+ public final static short RAND_NI = 1;
+
+ public final static short LE = 0;
+ public final static short EQ = 1;
+ public final static short GE = 2;
+ public final static short OF = 3;
+
+ public final static short MAX_WARN_COUNT = 20;
+
+ public final static float DEF_INFINITE = (float)1e24; /* limit for dynamic range */
+ public final static float DEF_EPSB = (float)5.01e-7; /* for rounding RHS values to 0 determine
+ infeasibility basis */
+ public final static float DEF_EPSEL = (float)1e-8; /* for rounding other values (vectors) to 0 */
+ public final static float DEF_EPSD = (float)1e-6; /* for rounding reduced costs to zero */
+ public final static float DEF_EPSILON = (float)1e-3; /* to determine if a float value is integer */
+
+ public final static float PREJ = (float)1e-3; /* pivot reject (try others first) */
+
+ public final static int ETA_START_SIZE = 10000; /* start size of array Eta. Realloced if needed */
+
+ static class Ref {
+ float value;
+ public Ref(float v) { value = v; }
+ }
+
+ //public static class Simplex {
+ /* Globals used by solver */
+ short JustInverted;
+ short Status;
+ short Doiter;
+ short DoInvert;
+ short Break_bb;
+
+ public short active; /*TRUE if the globals point to this structure*/
+ public short debug; /* ## Print B&B information */
+ public short trace; /* ## Print information on pivot selection */
+ public int rows; /* Nr of constraint rows in the problem */
+ int rows_alloc; /* The allocated memory for Rows sized data */
+ int columns_alloc;
+ int sum; /* The size of the variables + the slacks */
+ int sum_alloc;
+ int non_zeros; /* The number of elements in the sparce matrix*/
+ int mat_alloc; /* The allocated size for matrix sized
+ structures */
+ MatrixArray mat; /* mat_alloc :The sparse matrix */
+ MatrixArray alternate_mat; /* mat_alloc :The sparse matrix */
+ int[] col_end; /* columns_alloc+1 :Cend[i] is the index of the
+ first element after column i.
+ column[i] is stored in elements
+ col_end[i-1] to col_end[i]-1 */
+ int[] col_no; /* mat_alloc :From Row 1 on, col_no contains the
+ column nr. of the
+ nonzero elements, row by row */
+ short row_end_valid; /* true if row_end & col_no are valid */
+ int[] row_end; /* rows_alloc+1 :row_end[i] is the index of the
+ first element in Colno after row i */
+ float[] orig_rh; /* rows_alloc+1 :The RHS after scaling & sign
+ changing, but before `Bound transformation' */
+ float[] rh; /* rows_alloc+1 :As orig_rh, but after Bound
+ transformation */
+ float[] rhs; /* rows_alloc+1 :The RHS of the curent simplex
+ tableau */
+ float[] orig_upbo; /* sum_alloc+1 :Bound before transformations */
+ float[] orig_lowbo; /* " " */
+ float[] upbo; /* " " :Upper bound after transformation
+ & B&B work*/
+ float[] lowbo; /* " " :Lower bound after transformation
+ & B&B work */
+
+ short basis_valid; /* TRUE is the basis is still valid */
+ int[] bas; /* rows_alloc+1 :The basis column list */
+ short[] basis; /* sum_alloc+1 : basis[i] is TRUE if the column
+ is in the basis */
+ short[] lower; /* " " :TRUE is the variable is at its
+ lower bound (or in the basis), it is FALSE
+ if the variable is at its upper bound */
+
+ short eta_valid; /* TRUE if current Eta structures are valid */
+ int eta_alloc; /* The allocated memory for Eta */
+ int eta_size; /* The number of Eta columns */
+ int num_inv; /* The number of float pivots */
+ int max_num_inv; /* ## The number of float pivots between
+ reinvertions */
+ float[] eta_value; /* eta_alloc :The Structure containing the
+ values of Eta */
+ int[] eta_row_nr; /* " " :The Structure containing the Row
+ indexes of Eta */
+ int[] eta_col_end; /* rows_alloc + MaxNumInv : eta_col_end[i] is
+ the start index of the next Eta column */
+
+ short bb_rule; /* what rule for selecting B&B variables */
+
+ short break_at_int; /* TRUE if stop at first integer better than
+ break_value */
+ float break_value;
+
+ float obj_bound; /* ## Objective function bound for speedup of
+ B&B */
+ int iter; /* The number of iterations in the simplex
+ solver () */
+ int total_iter; /* The total number of iterations (B&B) (ILP)*/
+ int max_level; /* The Deepest B&B level of the last solution */
+ int total_nodes; /* total number of nodes processed in b&b */
+ public float[] solution; /* sum_alloc+1 :The Solution of the last LP,
+ 0 = The Optimal Value,
+ 1..rows The Slacks,
+ rows+1..sum The Variables */
+ public float[] best_solution; /* " " :The Best 'Integer' Solution */
+ float[] duals; /* rows_alloc+1 :The dual variables of the
+ last LP */
+
+ short maximise; /* TRUE if the goal is to maximise the
+ objective function */
+ short floor_first; /* TRUE if B&B does floor bound first */
+ short[] ch_sign; /* rows_alloc+1 :TRUE if the Row in the matrix
+ has changed sign
+ (a`x > b, x>=0) is translated to
+ s + -a`x = -b with x>=0, s>=0) */
+
+ int nr_lagrange; /* Nr. of Langrangian relaxation constraints */
+ float[][]lag_row; /* NumLagrange, columns+1:Pointer to pointer of
+ rows */
+ float[] lag_rhs; /* NumLagrange :Pointer to pointer of Rhs */
+ float[] lambda; /* NumLagrange :Lambda Values */
+ short[] lag_con_type; /* NumLagrange :TRUE if constraint type EQ */
+ float lag_bound; /* the lagrangian lower bound */
+
+ short valid; /* Has this lp pased the 'test' */
+ float infinite; /* ## numercal stuff */
+ float epsilon; /* ## */
+ float epsb; /* ## */
+ float epsd; /* ## */
+ float epsel; /* ## */
+
+ int Rows;
+ int columns;
+ int Sum;
+ int Non_zeros;
+ int Level;
+ MatrixArray Mat;
+ int[] Col_no;
+ int[] Col_end;
+ int[] Row_end;
+ float[] Orig_rh;
+ float[] Rh;
+ float[] Rhs;
+ float[] Orig_upbo;
+ float[] Orig_lowbo;
+ float[] Upbo;
+ float[] Lowbo;
+ int[] Bas;
+ short[] Basis;
+ short[] Lower;
+ int Eta_alloc;
+ int Eta_size;
+ float[] Eta_value;
+ int[] Eta_row_nr;
+ int[] Eta_col_end;
+ int Num_inv;
+ float[] Solution;
+ public float[] Best_solution;
+ float Infinite;
+ float Epsilon;
+ float Epsb;
+ float Epsd;
+ float Epsel;
+
+ float TREJ;
+ float TINV;
+
+ short Maximise;
+ short Floor_first;
+ float Extrad;
+
+ int Warn_count; /* used in CHECK version of rounding macro */
+
+ public Simplex (int nrows, int ncolumns, int matalloc) {
+ int nsum;
+ nsum=nrows+ncolumns;
+ rows=nrows;
+ columns=ncolumns;
+ sum=nsum;
+ rows_alloc=rows;
+ columns_alloc=columns;
+ sum_alloc=sum;
+ mat_alloc=matalloc;
+ eta_alloc=10000;
+ max_num_inv=DEFNUMINV;
+ col_no = new int[mat_alloc];
+ col_end = new int[columns + 1];
+ row_end = new int[rows + 1];
+ orig_rh = new float[rows + 1];
+ rh = new float[rows + 1];
+ rhs = new float[rows + 1];
+ orig_upbo = new float[sum + 1];
+ upbo = new float[sum + 1];
+ orig_lowbo = new float[sum + 1];
+ lowbo = new float[sum + 1];
+ bas = new int[rows+1];
+ basis = new short[sum + 1];
+ lower = new short[sum + 1];
+ eta_value = new float[eta_alloc];
+ eta_row_nr = new int[eta_alloc];
+ eta_col_end = new int[rows_alloc + max_num_inv];
+ solution = new float[sum + 1];
+ best_solution = new float[sum + 1];
+ duals = new float[rows + 1];
+ ch_sign = new short[rows + 1];
+ mat = new MatrixArray(mat_alloc);
+ alternate_mat = new MatrixArray(mat_alloc);
+ }
+
+ public void init(int ncolumns) {
+ int nsum;
+ int nrows = 0;
+ nsum=nrows+ncolumns;
+ active=FALSE;
+ debug=FALSE;
+ trace=FALSE;
+ rows=nrows;
+ columns=ncolumns;
+ sum=nsum;
+ obj_bound=DEF_INFINITE;
+ infinite=DEF_INFINITE;
+ epsilon=DEF_EPSILON;
+ epsb=DEF_EPSB;
+ epsd=DEF_EPSD;
+ epsel=DEF_EPSEL;
+ non_zeros=0;
+
+ for(int i = 0; i < col_end.length; i++) col_end[i] = 0;
+ for(int i = 0; i < rows + 1; i++) row_end[i] = 0;
+ for(int i = 0; i < rows + 1; i++) orig_rh[i] = 0;
+ for(int i = 0; i < rows + 1; i++) rh[i] = 0;
+ for(int i = 0; i < rows + 1; i++) rhs[i] = 0;
+ for(int i = 0; i <= sum; i++) orig_upbo[i]=infinite;
+ for(int i = 0; i < sum + 1; i++) upbo[i] = 0;
+ for(int i = 0; i < sum + 1; i++) orig_lowbo[i] = 0;
+ for(int i = 0; i < sum + 1; i++) lowbo[i] = 0;
+ for(int i = 0; i <= rows; i++) bas[i] = 0;
+ for(int i = 0; i <= sum; i++) basis[i] = 0;
+ for(int i = 0; i <= rows; i++) { bas[i]=i; basis[i]=TRUE; }
+ for(int i = rows + 1; i <= sum; i++) basis[i]=FALSE;
+ for(int i = 0 ; i <= sum; i++) lower[i]=TRUE;
+ for(int i = 0; i <= sum; i++) solution[i] = 0;
+ for(int i = 0; i <= sum; i++) best_solution[i] = 0;
+ for(int i = 0; i <= rows; i++) duals[i] = 0;
+ for(int i = 0; i <= rows; i++) ch_sign[i] = FALSE;
+
+ row_end_valid=FALSE;
+ bb_rule=FIRST_NI;
+ break_at_int=FALSE;
+ break_value=0;
+ iter=0;
+ total_iter=0;
+ basis_valid=TRUE;
+ eta_valid=TRUE;
+ eta_size=0;
+ nr_lagrange=0;
+ maximise = FALSE;
+ floor_first = TRUE;
+ valid = FALSE;
+ }
+
+ public void setObjective(float[] row, boolean maximize) {
+ for(int i=row.length-1; i>0; i--) row[i] = row[i-1];
+ row[0] = (float)0.0;
+ for(int j = 1; j <= columns; j++) {
+ int Row = 0;
+ int column = j;
+ float Value = row[j];
+ int elmnr, lastelm;
+
+ if(Row > rows || Row < 0) throw new Error("row out of range");
+ if(column > columns || column < 1) throw new Error("column out of range");
+
+ if (basis[column] == TRUE && Row > 0) basis_valid = FALSE;
+ eta_valid = FALSE;
+ elmnr = col_end[column-1];
+ while((elmnr < col_end[column]) ? (get_row_nr(mat, elmnr) != Row) : false) elmnr++;
+ if((elmnr != col_end[column]) ? (get_row_nr(mat, elmnr) == Row) : false ) {
+ if (ch_sign[Row] != FALSE) set_value(mat, elmnr, -Value);
+ else set_value(mat, elmnr, Value);
+ } else {
+ /* check if more space is needed for matrix */
+ if (non_zeros + 1 > mat_alloc) throw new Error("not enough mat space; this should not happen");
+ /* Shift the matrix */
+ lastelm=non_zeros;
+ for(int i = lastelm; i > elmnr ; i--) {
+ set_row_nr(mat,i,get_row_nr(mat,i-1));
+ set_value(mat,i,get_value(mat,i-1));
+ }
+ for(int i = column; i <= columns; i++) col_end[i]++;
+ /* Set new element */
+ set_row_nr(mat,elmnr, Row);
+ if (ch_sign[Row] != FALSE) set_value(mat, elmnr, -Value);
+ else set_value(mat, elmnr, Value);
+ row_end_valid=FALSE;
+ non_zeros++;
+ if (active != FALSE) Non_zeros=non_zeros;
+ }
+ }
+ if (maximize) {
+ if (maximise == FALSE) {
+ for(int i = 0; i < non_zeros; i++)
+ if(get_row_nr(mat, i)==0)
+ set_value(mat, i, get_value(mat,i)* (float)-1.0);
+ eta_valid=FALSE;
+ }
+ maximise=TRUE;
+ ch_sign[0]=TRUE;
+ if (active != FALSE) Maximise=TRUE;
+ } else {
+ if (maximise==TRUE) {
+ for(int i = 0; i < non_zeros; i++)
+ if(get_row_nr(mat, i)==0)
+ set_value(mat, i, get_value(mat,i) * (float)-1.0);
+ eta_valid=FALSE;
+ }
+ maximise=FALSE;
+ ch_sign[0]=FALSE;
+ if (active != FALSE) Maximise=FALSE;
+ }
+ }
+
+ public void add_constraint(float[] row, short constr_type, float rh) {
+ for(int i=row.length-1; i>0; i--) row[i] = row[i-1];
+ row[0] = (float)0.0;
+
+ MatrixArray newmat;
+ int elmnr;
+ int stcol;
+
+ newmat = alternate_mat;
+ for(int i = 0; i < non_zeros; i++) { set_row_nr(newmat,i, 0); set_value(newmat, i, 0); }
+ for(int i = 1; i <= columns; i++) if (row[i]!=0) non_zeros++;
+ if (non_zeros > mat_alloc) throw new Error("not enough mat space; this should not happen");
+ rows++;
+ sum++;
+ if(rows > rows_alloc) throw new Error("not enough rows; this should never happen");
+ if(constr_type==GE) ch_sign[rows] = TRUE;
+ else ch_sign[rows] = FALSE;
+
+ elmnr = 0;
+ stcol = 0;
+ for(int i = 1; i <= columns; i++) {
+ for(int j = stcol; j < col_end[i]; j++) {
+ set_row_nr(newmat,elmnr, get_row_nr(mat, j));
+ set_value(newmat, elmnr, get_value(mat,j));
+ elmnr++;
+ }
+ if(((i>=1 && i< columns && row[i]!=0)?TRUE:FALSE) != FALSE) {
+ if(ch_sign[rows] != FALSE) set_value(newmat, elmnr, -row[i]);
+ else set_value(newmat, elmnr, row[i]);
+ set_row_nr(newmat,elmnr, rows);
+ elmnr++;
+ }
+ stcol=col_end[i];
+ col_end[i]=elmnr;
+ }
+
+ alternate_mat = mat;
+ mat = newmat;
+
+ for(int i = sum ; i > rows; i--) {
+ orig_upbo[i]=orig_upbo[i-1];
+ orig_lowbo[i]=orig_lowbo[i-1];
+ basis[i]=basis[i-1];
+ lower[i]=lower[i-1];
+ }
+
+ for(int i = 1 ; i <= rows; i++) if(bas[i] >= rows) bas[i]++;
+
+ if(constr_type==LE || constr_type==GE) orig_upbo[rows]=infinite;
+ else if(constr_type==EQ) orig_upbo[rows]=0;
+ else throw new Error("Wrong constraint type\n");
+ orig_lowbo[rows]=0;
+
+ if(constr_type==GE && rh != 0) orig_rh[rows]=-rh;
+ else orig_rh[rows]=rh;
+
+ row_end_valid=FALSE;
+
+ bas[rows]=rows;
+ basis[rows]=TRUE;
+ lower[rows]=TRUE;
+
+ if (active != FALSE) set_globals();
+ eta_valid=FALSE;
+ }
+
+ public void bound_sum(int column1, int column2, float bound, short type, float[] scratch) {
+ for(int i=0; i columns || column < 1) throw new Error("column out of range");
+ if(value < orig_lowbo[rows + column]) throw new Error("UpperBound must be >= lowerBound");
+ eta_valid = FALSE;
+ orig_upbo[rows+column] = value;
+ }
+
+ public void set_lowbo(int column, float value) {
+ if(column > columns || column < 1) throw new Error("column out of range");
+ if(value > orig_upbo[rows + column]) throw new Error("UpperBound must be >= lowerBound");
+ eta_valid = FALSE;
+ orig_lowbo[rows+column] = value;
+ }
+
+ public void set_rh(int row, float value) {
+ if(row > rows || row < 0) throw new Error("Row out of Range");
+ if(row == 0) throw new Error("Warning: attempt to set RHS of objective function, ignored");
+ if (ch_sign[row] != FALSE) orig_rh[row] = -value;
+ else orig_rh[row] = value;
+ eta_valid = FALSE;
+ }
+
+ public void set_rh_vec(float[] rh) {
+ for(int i=1; i <= rows; i++)
+ if (ch_sign[i] != FALSE) orig_rh[i]=-rh[i];
+ else orig_rh[i]=rh[i];
+ eta_valid=FALSE;
+ }
+
+
+ public void set_constr_type(int row, short con_type) {
+ if (row > rows || row < 1) throw new Error("Row out of Range");
+ switch(con_type) {
+ case EQ:
+ orig_upbo[row]=0;
+ basis_valid=FALSE;
+ if (ch_sign[row] != FALSE) {
+ for(int i = 0; i < non_zeros; i++)
+ if (get_row_nr(mat, i)==row) set_value(mat, i, get_value(mat,i) * (float)-1);
+ eta_valid=FALSE;
+ ch_sign[row]=FALSE;
+ if (orig_rh[row]!=0) orig_rh[row]*=-1;
+ }
+ break;
+ case LE:
+ orig_upbo[row]=infinite;
+ basis_valid=FALSE;
+ if (ch_sign[row] != FALSE) {
+ for(int i = 0; i < non_zeros; i++)
+ if (get_row_nr(mat, i)==row) set_value(mat, i, get_value(mat,i) * (float)-1);
+ eta_valid=FALSE;
+ ch_sign[row]=FALSE;
+ if (orig_rh[row]!=0) orig_rh[row]*=-1;
+ }
+ break;
+ case GE:
+ orig_upbo[row]=infinite;
+ basis_valid=FALSE;
+ if (ch_sign[row] == FALSE) {
+ for(int i = 0; i < non_zeros; i++)
+ if (get_row_nr(mat, i)==row) set_value(mat, i, get_value(mat,i) * (float)-1);
+ eta_valid=FALSE;
+ ch_sign[row]=TRUE;
+ if (orig_rh[row]!=0) orig_rh[row]*=-1;
+ }
+ break;
+ default: throw new Error("Constraint type not (yet) implemented");
+ }
+ }
+
+ void set_globals() {
+ Rows = rows;
+ columns = columns;
+ Sum = Rows + columns;
+ Non_zeros = non_zeros;
+ Mat = mat;
+ Col_no = col_no;
+ Col_end = col_end;
+ Row_end = row_end;
+ Rh = rh;
+ Rhs = rhs;
+ Orig_rh = orig_rh;
+ Orig_upbo = orig_upbo;
+ Orig_lowbo = orig_lowbo;
+ Upbo = upbo;
+ Lowbo = lowbo;
+ Bas = bas;
+ Basis = basis;
+ Lower = lower;
+ Eta_alloc = eta_alloc;
+ Eta_size = eta_size;
+ Num_inv = num_inv;
+ Eta_value = eta_value;
+ Eta_row_nr = eta_row_nr;
+ Eta_col_end = eta_col_end;
+ Solution = solution;
+ Best_solution = best_solution;
+ Infinite = infinite;
+ Epsilon = epsilon;
+ Epsb = epsb;
+ Epsd = epsd;
+ Epsel = epsel;
+ TREJ = TREJ;
+ TINV = TINV;
+ Maximise = maximise;
+ Floor_first = floor_first;
+ active = TRUE;
+ }
+
+ private void ftran(int start, int end, float[] pcol) {
+ int k, r;
+ float theta;
+ for(int i = start; i <= end; i++) {
+ k = Eta_col_end[i] - 1;
+ r = Eta_row_nr[k];
+ theta = pcol[r];
+ if (theta != 0) for(int j = Eta_col_end[i - 1]; j < k; j++)
+ pcol[Eta_row_nr[j]] += theta * Eta_value[j];
+ pcol[r] *= Eta_value[k];
+ }
+ for(int i = 0; i <= Rows; i++) round(pcol[i], Epsel);
+ }
+
+ private void btran(float[] row) {
+ int k;
+ float f;
+ for(int i = Eta_size; i >= 1; i--) {
+ f = 0;
+ k = Eta_col_end[i] - 1;
+ for(int j = Eta_col_end[i - 1]; j <= k; j++) f += row[Eta_row_nr[j]] * Eta_value[j];
+ f = round(f, Epsel);
+ row[Eta_row_nr[k]] = f;
+ }
+ }
+
+ static int[] num = new int[65535];
+ static int[] rownum = new int[65535];
+ static int[] colnum = new int[65535];
+
+ short Isvalid() {
+ int row_nr;
+ if (row_end_valid == FALSE) {
+ for(int i = 0; i <= rows; i++) { num[i] = 0; rownum[i] = 0; }
+ for(int i = 0; i < non_zeros; i++) rownum[get_row_nr(mat, i)]++;
+ row_end[0] = 0;
+ for(int i = 1; i <= rows; i++) row_end[i] = row_end[i - 1] + rownum[i];
+ for(int i = 1; i <= columns; i++)
+ for(int j = col_end[i - 1]; j < col_end[i]; j++) {
+ row_nr = get_row_nr(mat, j);
+ if (row_nr != 0) {
+ num[row_nr]++;
+ col_no[row_end[row_nr - 1] + num[row_nr]] = i;
+ }
+ }
+ row_end_valid = TRUE;
+ }
+ if (valid != FALSE) return(TRUE);
+ for(int i = 0; i <= rows; i++) rownum[i] = 0;
+ for(int i = 0; i <= columns; i++) colnum[i] = 0;
+ for(int i = 1 ; i <= columns; i++)
+ for(int j = col_end[i - 1]; j < col_end[i]; j++) {
+ colnum[i]++;
+ rownum[get_row_nr(mat, j)]++;
+ }
+ for(int i = 1; i <= columns; i++)
+ if (colnum[i] == 0)
+ throw new Error("Warning: Variable " + i + " not used in any constaints\n");
+ valid = TRUE;
+ return(TRUE);
+ }
+
+ private void resize_eta() {
+ Eta_alloc *= 2;
+ throw new Error("eta undersized; this should never happen");
+ /*
+ float[] db_ptr = Eta_value;
+ Eta_value = new float[Eta_alloc];
+ System.arraycopy(db_ptr, 0, Eta_value, 0, db_ptr.length);
+ eta_value = Eta_value;
+
+ int[] int_ptr = Eta_row_nr;
+ Eta_row_nr = new int[Eta_alloc];
+ System.arraycopy(int_ptr, 0, Eta_row_nr, 0, int_ptr.length);
+ eta_row_nr = Eta_row_nr;
+ */
+ }
+
+ private void condensecol(int row_nr, float[] pcol) {
+ int elnr;
+ elnr = Eta_col_end[Eta_size];
+ if (elnr + Rows + 2 > Eta_alloc) resize_eta();
+ for(int i = 0; i <= Rows; i++)
+ if (i != row_nr && pcol[i] != 0) {
+ Eta_row_nr[elnr] = i;
+ Eta_value[elnr] = pcol[i];
+ elnr++;
+ }
+ Eta_row_nr[elnr] = row_nr;
+ Eta_value[elnr] = pcol[row_nr];
+ elnr++;
+ Eta_col_end[Eta_size + 1] = elnr;
+ }
+
+ private void addetacol() {
+ int k;
+ float theta;
+ int j = Eta_col_end[Eta_size];
+ Eta_size++;
+ k = Eta_col_end[Eta_size];
+ theta = 1 / (float) Eta_value[k - 1];
+ Eta_value[k - 1] = theta;
+ for(int i = j; i < k - 1; i++) Eta_value[i] *= -theta;
+ JustInverted = FALSE;
+ }
+
+ private void setpivcol(short lower, int varin, float[] pcol) {
+ int colnr;
+ float f;
+ if (lower != FALSE) f = 1;
+ else f = -1;
+ for(int i = 0; i <= Rows; i++) pcol[i] = 0;
+ if (varin > Rows) {
+ colnr = varin - Rows;
+ for(int i = Col_end[colnr - 1]; i < Col_end[colnr]; i++) pcol[get_row_nr(Mat, i)] = get_value(Mat,i) * f;
+ pcol[0] -= Extrad * f;
+ } else {
+ if (lower != FALSE) pcol[varin] = 1;
+ else pcol[varin] = -1;
+ }
+ ftran(1, Eta_size, pcol);
+ }
+
+ private void minoriteration(int colnr, int row_nr) {
+ int k, wk, varin, varout, elnr;
+ float piv = 0, theta;
+ varin = colnr + Rows;
+ elnr = Eta_col_end[Eta_size];
+ wk = elnr;
+ Eta_size++;
+ if (Extrad != 0) {
+ Eta_row_nr[elnr] = 0;
+ Eta_value[elnr] = -Extrad;
+ elnr++;
+ }
+ for(int j = Col_end[colnr - 1] ; j < Col_end[colnr]; j++) {
+ k = get_row_nr(Mat, j);
+ if (k == 0 && Extrad != 0) Eta_value[Eta_col_end[Eta_size -1]] += get_value(Mat,j);
+ else if (k != row_nr) {
+ Eta_row_nr[elnr] = k;
+ Eta_value[elnr] = get_value(Mat,j);
+ elnr++;
+ } else {
+ piv = get_value(Mat,j);
+ }
+ }
+ Eta_row_nr[elnr] = row_nr;
+ Eta_value[elnr] = 1 / (float) piv;
+ elnr++;
+ theta = Rhs[row_nr] / (float) piv;
+ Rhs[row_nr] = theta;
+ for(int i = wk; i < elnr - 1; i++) Rhs[Eta_row_nr[i]] -= theta * Eta_value[i];
+ varout = Bas[row_nr];
+ Bas[row_nr] = varin;
+ Basis[varout] = FALSE;
+ Basis[varin] = TRUE;
+ for(int i = wk; i < elnr - 1; i++) Eta_value[i] /= - (float) piv;
+ Eta_col_end[Eta_size] = elnr;
+ }
+
+ private void rhsmincol(float theta, int row_nr, int varin) {
+ int varout;
+ float f;
+ if (row_nr > Rows + 1) {
+ System.err.println("Error: rhsmincol called with row_nr: " + row_nr + ", rows: " + Rows + "\n");
+ System.err.println("This indicates numerical instability\n");
+ }
+ int j = Eta_col_end[Eta_size];
+ int k = Eta_col_end[Eta_size + 1];
+ for(int i = j; i < k; i++) {
+ f = Rhs[Eta_row_nr[i]] - theta * Eta_value[i];
+ f = round(f, Epsb);
+ Rhs[Eta_row_nr[i]] = f;
+ }
+ Rhs[row_nr] = theta;
+ varout = Bas[row_nr];
+ Bas[row_nr] = varin;
+ Basis[varout] = FALSE;
+ Basis[varin] = TRUE;
+ }
+
+ private static int[] rownum_ = new int[65535];
+ private static int[] colnum_ = new int[65535];
+ private static int[] col = new int[65535];
+ private static int[] row = new int[65535];
+ private static float[] pcol = new float[65535];
+ private static short[] frow = new short[65535];
+ private static short[] fcol = new short[65535];
+
+ void invert() {
+ int v, wk, numit, varnr, row_nr, colnr, varin;
+ float theta;
+
+ for(int i = 0; i <= Rows; i++) rownum_[i] = 0;
+ for(int i = 0; i <= Rows; i++) col[i] = 0;
+ for(int i = 0; i <= Rows; i++) row[i] = 0;
+ for(int i = 0; i <= Rows; i++) pcol[i] = 0;
+ for(int i = 0; i <= Rows; i++) frow[i] = TRUE;
+ for(int i = 0; i < columns; i++) fcol[i] = FALSE;
+ for(int i = 0; i <= columns; i++) colnum_[i] = 0;
+
+ for(int i = 0; i <= Rows; i++)
+ if (Bas[i] > Rows) fcol[Bas[i] - Rows - 1] = TRUE;
+ else frow[Bas[i]] = FALSE;
+
+ for(int i = 1; i <= Rows; i++)
+ if (frow[i] != FALSE)
+ for(int j = Row_end[i - 1] + 1; j <= Row_end[i]; j++) {
+ wk = Col_no[j];
+ if (fcol[wk - 1] != FALSE) {
+ colnum_[wk]++;
+ rownum_[i - 1]++;
+ }
+ }
+
+ for(int i = 1; i <= Rows; i++) Bas[i] = i;
+ for(int i = 1; i <= Rows; i++) Basis[i] = TRUE;
+ for(int i = 1; i <= columns; i++) Basis[i + Rows] = FALSE;
+ for(int i = 0; i <= Rows; i++) Rhs[i] = Rh[i];
+ for(int i = 1; i <= columns; i++) {
+ varnr = Rows + i;
+ if (Lower[varnr] == FALSE) {
+ theta = Upbo[varnr];
+ for(int j = Col_end[i - 1]; j < Col_end[i]; j++)
+ Rhs[get_row_nr(Mat, j)] -= theta * get_value(Mat,j);
+ }
+ }
+ for(int i = 1; i <= Rows; i++) if (Lower[i] == FALSE) Rhs[i] -= Upbo[i];
+ Eta_size = 0;
+ v = 0;
+ row_nr = 0;
+ Num_inv = 0;
+ numit = 0;
+ while(v < Rows) {
+ int j;
+ row_nr++;
+ if (row_nr > Rows) row_nr = 1;
+ v++;
+ if (rownum_[row_nr - 1] == 1)
+ if (frow[row_nr] != FALSE) {
+ v = 0;
+ j = Row_end[row_nr - 1] + 1;
+ while(fcol[Col_no[j] - 1] == FALSE) j++;
+ colnr = Col_no[j];
+ fcol[colnr - 1] = FALSE;
+ colnum_[colnr] = 0;
+ for(j = Col_end[colnr - 1]; j < Col_end[colnr]; j++)
+ if (frow[get_row_nr(Mat, j)] != FALSE)
+ rownum_[get_row_nr(Mat, j) - 1]--;
+ frow[row_nr] = FALSE;
+ minoriteration(colnr, row_nr);
+ }
+ }
+ v = 0;
+ colnr = 0;
+ while(v < columns) {
+ int j;
+ colnr++;
+ if (colnr > columns) colnr = 1;
+ v++;
+ if (colnum_[colnr] == 1)
+ if (fcol[colnr - 1] != FALSE) {
+ v = 0;
+ j = Col_end[colnr - 1] + 1;
+ while(frow[get_row_nr(Mat, j - 1)] == FALSE) j++;
+ row_nr = get_row_nr(Mat, j - 1);
+ frow[row_nr] = FALSE;
+ rownum_[row_nr - 1] = 0;
+ for(j = Row_end[row_nr - 1] + 1; j <= Row_end[row_nr]; j++)
+ if (fcol[Col_no[j] - 1] != FALSE)
+ colnum_[Col_no[j]]--;
+ fcol[colnr - 1] = FALSE;
+ numit++;
+ col[numit - 1] = colnr;
+ row[numit - 1] = row_nr;
+ }
+ }
+ for(int j = 1; j <= columns; j++)
+ if (fcol[j - 1] != FALSE) {
+ fcol[j - 1] = FALSE;
+ setpivcol(Lower[Rows + j], j + Rows, pcol);
+ row_nr = 1;
+ while((frow[row_nr] == FALSE || pcol[row_nr] == FALSE) && row_nr <= Rows)
+ row_nr++; /* this sometimes sets row_nr to Rows + 1 and makes
+ rhsmincol crash. Solved in 2.0? MB */
+ if (row_nr == Rows + 1) throw new Error("Inverting failed");
+ frow[row_nr] = FALSE;
+ condensecol(row_nr, pcol);
+ theta = Rhs[row_nr] / (float) pcol[row_nr];
+ rhsmincol(theta, row_nr, Rows + j);
+ addetacol();
+ }
+ for(int i = numit - 1; i >= 0; i--) {
+ colnr = col[i];
+ row_nr = row[i];
+ varin = colnr + Rows;
+ for(int j = 0; j <= Rows; j++) pcol[j] = 0;
+ for(int j = Col_end[colnr - 1]; j < Col_end[colnr]; j++) pcol[get_row_nr(Mat, j)] = get_value(Mat,j);
+ pcol[0] -= Extrad;
+ condensecol(row_nr, pcol);
+ theta = Rhs[row_nr] / (float) pcol[row_nr];
+ rhsmincol(theta, row_nr, varin);
+ addetacol();
+ }
+ for(int i = 1; i <= Rows; i++) Rhs[i] = round(Rhs[i], Epsb);
+ JustInverted = TRUE;
+ DoInvert = FALSE;
+ }
+
+ private short colprim(Ref colnr, short minit, float[] drow) {
+ int varnr;
+ float f, dpiv;
+ dpiv = -Epsd;
+ colnr.value = 0;
+ if (minit == FALSE) {
+ for(int i = 1; i <= Sum; i++) drow[i] = 0;
+ drow[0] = 1;
+ btran(drow);
+ for(int i = 1; i <= columns; i++) {
+ varnr = Rows + i;
+ if (Basis[varnr] == FALSE)
+ if (Upbo[varnr] > 0) {
+ f = 0;
+ for(int j = Col_end[i - 1]; j < Col_end[i]; j++) f += drow[get_row_nr(Mat, j)] * get_value(Mat,j);
+ drow[varnr] = f;
+ }
+ }
+ for(int i = 1; i <= Sum; i++) drow[i] = round(drow[i], Epsd);
+ }
+ for(int i = 1; i <= Sum; i++)
+ if (Basis[i] == FALSE)
+ if (Upbo[i] > 0) {
+ if (Lower[i] != FALSE) f = drow[i];
+ else f = -drow[i];
+ if (f < dpiv) {
+ dpiv = f;
+ colnr.value = i;
+ }
+ }
+ if (colnr.value == 0) {
+ Doiter = FALSE;
+ DoInvert = FALSE;
+ Status = OPTIMAL;
+ }
+ return(colnr.value > 0 ? (short)1 : (short)0);
+ }
+
+ private short rowprim(int colnr, Ref row_nr, Ref theta, float[] pcol) {
+ float f = 0, quot;
+ row_nr.value = 0;
+ theta.value = Infinite;
+ for(int i = 1; i <= Rows; i++) {
+ f = pcol[i];
+ if (Math.abs(f) < TREJ) f = 0;
+ if (f != 0) {
+ quot = 2 * Infinite;
+ if (f > 0) quot = Rhs[i] / (float) f;
+ else if (Upbo[Bas[i]] < Infinite) quot = (Rhs[i] - Upbo[Bas[i]]) / (float) f;
+ round(quot, Epsel);
+ if (quot < theta.value) {
+ theta.value = quot;
+ row_nr.value = i;
+ }
+ }
+ }
+ if (row_nr.value == 0)
+ for(int i = 1; i <= Rows; i++) {
+ f = pcol[i];
+ if (f != 0) {
+ quot = 2 * Infinite;
+ if (f > 0) quot = Rhs[i] / (float) f;
+ else if (Upbo[Bas[i]] < Infinite) quot = (Rhs[i] - Upbo[Bas[i]]) / (float) f;
+ quot = round(quot, Epsel);
+ if (quot < theta.value) {
+ theta.value = quot;
+ row_nr.value = i;
+ }
+ }
+ }
+
+ if (theta.value < 0) throw new Error("Warning: Numerical instability, qout = " + theta.value);
+ if (row_nr.value == 0) {
+ if (Upbo[colnr] == Infinite) {
+ Doiter = FALSE;
+ DoInvert = FALSE;
+ Status = UNBOUNDED;
+ } else {
+ int i = 1;
+ while(pcol[i] >= 0 && i <= Rows) i++;
+ if (i > Rows) {
+ Lower[colnr] = FALSE;
+ Rhs[0] += Upbo[colnr]*pcol[0];
+ Doiter = FALSE;
+ DoInvert = FALSE;
+ } else if (pcol[i]<0) {
+ row_nr.value = i;
+ }
+ }
+ }
+ if (row_nr.value > 0) Doiter = TRUE;
+ return((row_nr.value > 0) ? (short)1 : (short)0);
+ }
+
+ private short rowdual(Ref row_nr) {
+ int i;
+ float f, g, minrhs;
+ short artifs;
+ row_nr.value = 0;
+ minrhs = -Epsb;
+ i = 0;
+ artifs = FALSE;
+ while(i < Rows && artifs == FALSE) {
+ i++;
+ f = Upbo[Bas[i]];
+ if (f == 0 && (Rhs[i] != 0)) {
+ artifs = TRUE;
+ row_nr.value = i;
+ } else {
+ if (Rhs[i] < f - Rhs[i]) g = Rhs[i];
+ else g = f - Rhs[i];
+ if (g < minrhs) {
+ minrhs = g;
+ row_nr.value = i;
+ }
+ }
+ }
+ return(row_nr.value > 0 ? (short)1 : (short)0);
+ }
+
+ private short coldual(int row_nr, Ref colnr, short minit, float[] prow, float[] drow) {
+ int r, varnr;
+ float theta, quot, pivot, d, f, g;
+ Doiter = FALSE;
+ if (minit == FALSE) {
+ for(int i = 0; i <= Rows; i++) {
+ prow[i] = 0;
+ drow[i] = 0;
+ }
+ drow[0] = 1;
+ prow[row_nr] = 1;
+ for(int i = Eta_size; i >= 1; i--) {
+ d = 0;
+ f = 0;
+ r = Eta_row_nr[Eta_col_end[i] - 1];
+ for(int j = Eta_col_end[i - 1]; j < Eta_col_end[i]; j++) {
+ /* this is where the program consumes most cpu time */
+ f += prow[Eta_row_nr[j]] * Eta_value[j];
+ d += drow[Eta_row_nr[j]] * Eta_value[j];
+ }
+ f = round(f, Epsel);
+ prow[r] = f;
+ d = round(d, Epsel);
+ drow[r] = d;
+ }
+ for(int i = 1; i <= columns; i++) {
+ varnr = Rows + i;
+ if (Basis[varnr] == FALSE) {
+ d = - Extrad * drow[0];
+ f = 0;
+ for(int j = Col_end[i - 1]; j < Col_end[i]; j++) {
+ d = d + drow[get_row_nr(Mat, j)] * get_value(Mat,j);
+ f = f + prow[get_row_nr(Mat, j)] * get_value(Mat,j);
+ }
+ drow[varnr] = d;
+ prow[varnr] = f;
+ }
+ }
+ for(int i = 0; i <= Sum; i++) {
+ prow[i] = round(prow[i], Epsel);
+ drow[i] = round(drow[i], Epsd);
+ }
+ }
+ if (Rhs[row_nr] > Upbo[Bas[row_nr]]) g = -1;
+ else g = 1;
+ pivot = 0;
+ colnr.value = 0;
+ theta = Infinite;
+ for(int i = 1; i <= Sum; i++) {
+ if (Lower[i] != FALSE) d = prow[i] * g;
+ else d = -prow[i] * g;
+ if ((d < 0) && (Basis[i] == FALSE) && (Upbo[i] > 0)) {
+ if (Lower[i] == FALSE) quot = -drow[i] / (float) d;
+ else quot = drow[i] / (float) d;
+ if (quot < theta) {
+ theta = quot;
+ pivot = d;
+ colnr.value = i;
+ } else if ((quot == theta) && (Math.abs(d) > Math.abs(pivot))) {
+ pivot = d;
+ colnr.value = i;
+ }
+ }
+ }
+ if (colnr.value > 0) Doiter = TRUE;
+ return(colnr.value > 0 ? (short)1 : (short)0);
+ }
+
+ private void iteration(int row_nr, int varin, Ref theta, float up, Ref minit, Ref low, short primal,float[] pcol) {
+ int k, varout;
+ float f;
+ float pivot;
+ iter++;
+ minit.value = theta.value > (up + Epsb) ? 1 : 0;
+ if (minit.value != 0) {
+ theta.value = up;
+ low.value = low.value == 0 ? 1 : 0;
+ }
+ k = Eta_col_end[Eta_size + 1];
+ pivot = Eta_value[k - 1];
+ for(int i = Eta_col_end[Eta_size]; i < k; i++) {
+ f = Rhs[Eta_row_nr[i]] - theta.value * Eta_value[i];
+ f = round(f, Epsb);
+ Rhs[Eta_row_nr[i]] = f;
+ }
+ if (minit.value == 0) {
+ Rhs[row_nr] = theta.value;
+ varout = Bas[row_nr];
+ Bas[row_nr] = varin;
+ Basis[varout] = FALSE;
+ Basis[varin] = TRUE;
+ if (primal != FALSE && pivot < 0) Lower[varout] = FALSE;
+ if (low.value == 0 && up < Infinite) {
+ low.value = TRUE;
+ Rhs[row_nr] = up - Rhs[row_nr];
+ for(int i = Eta_col_end[Eta_size]; i < k; i++) Eta_value[i] = -Eta_value[i];
+ }
+ addetacol();
+ Num_inv++;
+ }
+ }
+
+ static float[] drow = new float[65535];
+ static float[] prow = new float[65535];
+ static float[] Pcol = new float[65535];
+
+ private int solvelp() {
+ int varnr;
+ float f = 0, theta = 0;
+ short primal;
+ short minit;
+ int colnr, row_nr;
+ colnr = 0;
+ row_nr = 0;
+ short flag;
+ Ref ref1, ref2, ref3;
+ ref1 = new Ref(0);
+ ref2 = new Ref(0);
+ ref3 = new Ref(0);
+
+ for(int i = 0; i <= Sum; i++) { drow[i] = 0; prow[i] = 0; }
+ for(int i = 0; i <= Rows; i++) Pcol[i] = 0;
+ iter = 0;
+ minit = FALSE;
+ Status = RUNNING;
+ DoInvert = FALSE;
+ Doiter = FALSE;
+ primal = TRUE;
+ for(int i = 0; i != Rows && primal != FALSE;) {
+ i++;
+ primal = (Rhs[i] >= 0 && Rhs[i] <= Upbo[Bas[i]]) ? (short)1: (short)0;
+ }
+ if (primal == FALSE) {
+ drow[0] = 1;
+ for(int i = 1; i <= Rows; i++) drow[i] = 0;
+ Extrad = 0;
+ for(int i = 1; i <= columns; i++) {
+ varnr = Rows + i;
+ drow[varnr] = 0;
+ for(int j = Col_end[i - 1]; j < Col_end[i]; j++)
+ if (drow[get_row_nr(Mat, j)] != 0)
+ drow[varnr] += drow[get_row_nr(Mat, j)] * get_value(Mat,j);
+ if (drow[varnr] < Extrad) Extrad = drow[varnr];
+ }
+ } else {
+ Extrad = 0;
+ }
+ minit = FALSE;
+ while(Status == RUNNING) {
+ Doiter = FALSE;
+ DoInvert = FALSE;
+ construct_solution(Solution);
+ if (primal != FALSE) {
+ ref1.value = colnr;
+ flag = colprim(ref1, minit, drow);
+ colnr = (int)ref1.value;
+ if (flag != FALSE) {
+ setpivcol(Lower[colnr], colnr, Pcol);
+ ref1.value = row_nr;
+ ref2.value = theta;
+ flag = rowprim(colnr, ref1, ref2, Pcol);
+ row_nr = (int)ref1.value;
+ theta = ref2.value;
+ if (flag != FALSE) condensecol(row_nr, Pcol);
+ }
+ } else {
+ if (minit == FALSE) {
+ ref1.value = row_nr;
+ flag = rowdual(ref1);
+ row_nr = (int)ref1.value;
+ }
+ if (row_nr > 0) {
+ ref1.value = colnr;
+ flag = coldual(row_nr, ref1, minit, prow, drow);
+ colnr = (int)ref1.value;
+ if (flag != FALSE) {
+ setpivcol(Lower[colnr], colnr, Pcol);
+ /* getting div by zero here ... MB */
+ if (Pcol[row_nr] == 0) {
+ throw new Error("An attempt was made to divide by zero (Pcol[" + row_nr + "])");
+ } else {
+ condensecol(row_nr, Pcol);
+ f = Rhs[row_nr] - Upbo[Bas[row_nr]];
+ if (f > 0) {
+ theta = f / (float) Pcol[row_nr];
+ if (theta <= Upbo[colnr])
+ Lower[Bas[row_nr]] = (Lower[Bas[row_nr]] == FALSE)? (short)1:(short)0;
+ } else theta = Rhs[row_nr] / (float) Pcol[row_nr];
+ }
+ } else Status = INFEASIBLE;
+ } else {
+ primal = TRUE;
+ Doiter = FALSE;
+ Extrad = 0;
+ DoInvert = TRUE;
+ }
+ }
+ if (Doiter != FALSE) {
+ ref1.value = theta;
+ ref2.value = minit;
+ ref3.value = Lower[colnr];
+ iteration(row_nr, colnr, ref1, Upbo[colnr], ref2, ref3, primal, Pcol);
+ theta = ref1.value;
+ minit = (short)ref2.value;
+ Lower[colnr] = (short)ref3.value;
+ }
+ if (Num_inv >= max_num_inv) DoInvert = TRUE;
+ if (DoInvert != FALSE) invert();
+ }
+ total_iter += iter;
+ return(Status);
+ }
+
+ private void construct_solution(float[] sol) {
+ float f;
+ int basi;
+ for(int i = 0; i <= Rows; i++) sol[i] = 0;
+ for(int i = Rows + 1; i <= Sum; i++) sol[i] = Lowbo[i];
+ for(int i = 1; i <= Rows; i++) {
+ basi = Bas[i];
+ if (basi > Rows) sol[basi] += Rhs[i];
+ }
+ for(int i = Rows + 1; i <= Sum; i++)
+ if (Basis[i] == FALSE && Lower[i] == FALSE)
+ sol[i] += Upbo[i];
+ for(int j = 1; j <= columns; j++) {
+ f = sol[Rows + j];
+ if (f != 0)
+ for(int i = Col_end[j - 1]; i < Col_end[j]; i++)
+ sol[get_row_nr(Mat, i)] += f * get_value(Mat,i);
+ }
+ for(int i = 0; i <= Rows; i++) {
+ if (Math.abs(sol[i]) < Epsb) sol[i] = 0;
+ else if (ch_sign[i] != FALSE) sol[i] = -sol[i];
+ }
+ }
+
+ private void calculate_duals() {
+ for(int i = 1; i <= Rows; i++) duals[i] = 0;
+ duals[0] = 1;
+ btran(duals);
+ for(int i = 1; i <= Rows; i++) {
+ if (basis[i] != FALSE) duals[i] = 0;
+ else if ( ch_sign[0] == ch_sign[i]) duals[i] = -duals[i];
+ }
+ }
+
+ private static Random rdm = new Random();
+
+ private int milpsolve(float[] upbo, float[] lowbo, short[] sbasis, short[] slower, int[] sbas) {
+ int failure, notint, is_worse;
+ float theta, tmpfloat;
+ notint = 0;
+
+ if (Break_bb != FALSE) return(BREAK_BB);
+ Level++;
+ total_nodes++;
+ if (Level > max_level) max_level = Level;
+ System.arraycopy(upbo, 0, Upbo, 0, Sum + 1);
+ System.arraycopy(lowbo, 0, Lowbo, 0, Sum + 1);
+ System.arraycopy(sbasis, 0, Basis, 0, Sum + 1);
+ System.arraycopy(slower, 0, Lower, 0, Sum + 1);
+ System.arraycopy(sbas, 0, Bas, 0, Rows + 1);
+ System.arraycopy(Orig_rh, 0, Rh, 0, Rows + 1);
+ if (eta_valid == FALSE) {
+ for(int i = 1; i <= columns; i++)
+ if (Lowbo[Rows + i] != 0) {
+ theta = Lowbo[ Rows + i];
+ if (Upbo[Rows + i]= Best_solution[0]) ? 1:0;
+ if (is_worse != FALSE) {
+ Level--;
+ return(MILP_FAIL);
+ }
+ /* check if solution contains enough ints */
+ if (bb_rule == FIRST_NI) {
+ notint = 0;
+ int i = Rows + 1;
+ while(i <= Sum && notint == 0) i++;
+ }
+ if (bb_rule == RAND_NI) {
+ int nr_not_int, select_not_int;
+ nr_not_int = 0;
+ for(int i = Rows + 1; i <= Sum; i++)
+ if (nr_not_int == 0) notint = 0;
+ else {
+ select_not_int=(rdm.nextInt() % nr_not_int) + 1;
+ i = Rows + 1;
+ while(select_not_int > 0) i++;
+ notint = i - 1;
+ }
+ }
+ if (notint != FALSE) throw new Error("integer linear programming not supported");
+ if (Maximise != FALSE) is_worse = (Solution[0] < Best_solution[0]) ? 1:0;
+ else is_worse = (Solution[0] > Best_solution[0]) ? 1:0;
+ if (is_worse == FALSE) {
+ System.arraycopy(Solution, 0, Best_solution, 0, Sum + 1);
+ calculate_duals();
+ if (break_at_int != FALSE) {
+ if (Maximise != FALSE && (Best_solution[0] > break_value)) Break_bb = TRUE;
+ if (Maximise == FALSE && (Best_solution[0] < break_value)) Break_bb = TRUE;
+ }
+ }
+ }
+ Level--;
+ return(failure);
+ }
+
+ public int solve() {
+ int result = FAILURE;
+ if (active == FALSE) set_globals();
+ total_iter = 0;
+ max_level = 1;
+ total_nodes = 0;
+ if (Isvalid() != FALSE) {
+ if (Maximise != FALSE && obj_bound == Infinite) Best_solution[0]=-Infinite;
+ else if (Maximise == FALSE && obj_bound==-Infinite) Best_solution[0] = Infinite;
+ else Best_solution[0] = obj_bound;
+ Level = 0;
+ if (basis_valid == FALSE) {
+ for(int i = 0; i <= rows; i++) {
+ basis[i] = TRUE;
+ bas[i] = i;
+ }
+ for(int i = rows+1; i <= sum; i++) basis[i] = FALSE;
+ for(int i = 0; i <= sum; i++) lower[i] = TRUE;
+ basis_valid = TRUE;
+ }
+ eta_valid = FALSE;
+ Break_bb = FALSE;
+ result = milpsolve(Orig_upbo, Orig_lowbo, Basis, Lower, Bas);
+ eta_size = Eta_size;
+ eta_alloc = Eta_alloc;
+ num_inv = Num_inv;
+ }
+ for(int i = 0; i < maxmat; i++) { set_row_nr(mat,i, 0); set_value(mat, i, 0); }
+ for(int i = 0; i < maxmat; i++) col_no[i] = 0;
+
+ // FIXME: not sure about this
+ for(int i = 0; i < Eta_size; i++) eta_value[i] = 0;
+ for(int i = 0; i < Eta_size; i++) eta_row_nr[i] = 0;
+ for(int i = 0; i < Eta_size; i++) eta_col_end[i] = 0;
+
+ maxmat = 0;
+ return(result);
+ }
+
+ private int maxmat = 0;
+ private final static float round( float val, float eps) { return (Math.abs(val) < eps) ? 0 : val; }
+ int get_row_nr(MatrixArray m, int i) { return m.row_nr[i]; }
+ void set_row_nr(MatrixArray m, int i, int val) { m.row_nr[i] = val; maxmat = Math.max(i, maxmat); }
+ float get_value(MatrixArray m, int i) { return m.value[i]; }
+ void set_value(MatrixArray m, int i, float val) { m.value[i] = val; maxmat = Math.max(i, maxmat); }
+ public static class MatrixArray {
+ public int[] row_nr;
+ public float[] value;
+ public final int length;
+ public MatrixArray(int length) { row_nr = new int[length]; value = new float[length]; this.length = length; }
+ }
+
+}
+
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/Task.java b/upstream/org.ibex.core/build/java/org/ibex/util/Task.java
new file mode 100644
index 0000000..e710724
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/Task.java
@@ -0,0 +1,8 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.util;
+import java.io.IOException;
+import org.ibex.js.*;
+
+public interface Task {
+ public abstract void perform() throws IOException, JSExn;
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/Vec.java b/upstream/org.ibex.core/build/java/org/ibex/util/Vec.java
new file mode 100644
index 0000000..71a0437
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/Vec.java
@@ -0,0 +1,221 @@
+// Copyright (C) 2003 Adam Megacz all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.ibex.util;
+
+import java.io.*;
+
+/**
+ * An unsynchronized Vector implementation; same semantics as
+ * java.util.Vector. Useful for JDK1.1 platforms that don't have
+ * java.util.ArrayList.
+ *
+ * May contain nulls.
+ *
+ * @see java.util.Vector
+ */
+public final class Vec implements Serializable {
+
+ private Object[] store;
+ private int size = 0;
+
+ public Vec() { this(10); }
+ public Vec(int i) { store = new Object[i]; }
+ public Vec(int i, Object[] store) { size = i; this.store = store; }
+
+ private void grow() { grow(store.length * 2); }
+ private void grow(int newsize) {
+ Object[] newstore = new Object[newsize];
+ System.arraycopy(store, 0, newstore, 0, size);
+ store = newstore;
+ }
+
+ public void removeAllElements() {
+ for(int i=0; i= store.length - 1) grow();
+ store[size++] = o;
+ }
+
+ public Object peek() {
+ return lastElement();
+ }
+
+ public Object elementAt(int i) {
+ return store[i];
+ }
+
+ public Object lastElement() {
+ if (size == 0) return null;
+ return store[size - 1];
+ }
+
+ public void push(Object o) { addElement(o); }
+ public Object pop() {
+ Object ret = lastElement();
+ if (size > 0) store[size--] = null;
+ return ret;
+ }
+
+ public int size() { return size; }
+
+ public void setSize(int newSize) {
+ if (newSize < 0) throw new RuntimeException("tried to set size to negative value");
+ if (newSize > store.length) grow(newSize * 2);
+ if (newSize < size)
+ for(int i=newSize; i= size || i < 0) throw new RuntimeException("tried to remove an element outside the vector's limits");
+ for(int j=i; j= size) setSize(i);
+ store[i] = o;
+ }
+
+ public void removeElement(Object o) {
+ int idx = indexOf(o);
+ if (idx != -1) removeElementAt(idx);
+ }
+
+ public void insertElementAt(Object o, int at) {
+ if (size == store.length) grow();
+ for(int i=size; i>at; i--)
+ store[i] = store[i-1];
+ store[at] = o;
+ size++;
+ }
+
+ public interface CompareFunc {
+ public int compare(Object a, Object b);
+ }
+
+ public void sort(CompareFunc c) {
+ sort(this, null, c, 0, size-1);
+ }
+
+ public static void sort(Vec a, Vec b, CompareFunc c) {
+ if (b != null && a.size != b.size) throw new IllegalArgumentException("Vec a and b must be of equal size");
+ sort(a, b, c, 0, a.size-1);
+ }
+
+ private static final void sort(Vec a, Vec b, CompareFunc c, int start, int end) {
+ Object tmpa, tmpb = null;
+ if(start >= end) return;
+ if(end-start <= 6) {
+ for(int i=start+1;i<=end;i++) {
+ tmpa = a.store[i];
+ if (b != null) tmpb = b.store[i];
+ int j;
+ for(j=i-1;j>=start;j--) {
+ if(c.compare(a.store[j],tmpa) <= 0) break;
+ a.store[j+1] = a.store[j];
+ if (b != null) b.store[j+1] = b.store[j];
+ }
+ a.store[j+1] = tmpa;
+ if (b != null) b.store[j+1] = tmpb;
+ }
+ return;
+ }
+
+ Object pivot = a.store[end];
+ int lo = start - 1;
+ int hi = end;
+
+ do {
+ while(c.compare(a.store[++lo],pivot) < 0) { }
+ while((hi > lo) && c.compare(a.store[--hi],pivot) > 0) { }
+ swap(a, lo,hi);
+ if (b != null) swap(b, lo,hi);
+ } while(lo < hi);
+
+ swap(a, lo,end);
+ if (b != null) swap(b, lo,end);
+
+ sort(a, b, c, start, lo-1);
+ sort(a, b, c, lo+1, end);
+ }
+
+ private static final void swap(Vec vec, int a, int b) {
+ if(a != b) {
+ Object tmp = vec.store[a];
+ vec.store[a] = vec.store[b];
+ vec.store[b] = tmp;
+ }
+ }
+
+ public static final void sortInts(int[] a, int start, int end) {
+ int tmpa;
+ if(start >= end) return;
+ if(end-start <= 6) {
+ for(int i=start+1;i<=end;i++) {
+ tmpa = a[i];
+ int j;
+ for(j=i-1;j>=start;j--) {
+ if(a[j] <= tmpa) break;
+ a[j+1] = a[j];
+ }
+ a[j+1] = tmpa;
+ }
+ return;
+ }
+
+ int pivot = a[end];
+ int lo = start - 1;
+ int hi = end;
+
+ do {
+ while(a[++lo] < pivot) { }
+ while((hi > lo) && a[--hi] > pivot) { }
+ swapInts(a, lo, hi);
+ } while(lo < hi);
+ swapInts(a, lo, end);
+ sortInts(a, start, lo-1);
+ sortInts(a, lo+1, end);
+ }
+
+ private static final void swapInts(int[] vec, int a, int b) {
+ if(a != b) {
+ int tmp = vec[a];
+ vec[a] = vec[b];
+ vec[b] = tmp;
+ }
+ }
+}
diff --git a/upstream/org.ibex.core/build/java/org/ibex/util/XML.java b/upstream/org.ibex.core/build/java/org/ibex/util/XML.java
new file mode 100644
index 0000000..d0775c7
--- /dev/null
+++ b/upstream/org.ibex.core/build/java/org/ibex/util/XML.java
@@ -0,0 +1,1174 @@
+// Copyright (C) 2003 Adam Megacz all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.ibex.util;
+
+import java.io.Reader;
+import java.io.IOException;
+import java.io.EOFException;
+
+/**
+ * An Event-Driving, Non-Validating XML Parser with Namespace support.
+ *
+ * A subclass can implement the abstract functions for receiving details
+ * about an xml file as it is parsed. To initate a parse, use the parse()
+ * function.
+ *
+ *
Implementation Notes
+ *
As the parser traverses into an element, it adds it to the linked list
+ * called elements. However, elements has been pre-filled
+ * with instances of the Element inner class. So in the vast majority of
+ * cases, the pointer current is moved along one, and the values for the
+ * new element are filled into the current object.
+ *
+ *
This parser supports all the unicode ranges required by the XML
+ * Specification. However, it is optimised for well-formed ASCII documents.
+ * Documents containing unicode Names and Attributes will take much longer
+ * to process, and invalid documents (badly formed Names or invalid attributes)
+ * will be run through a test on every single unicode character range before
+ * being declared invalid.
+ *
+ *
+ *
Each time the buffer offset off is moved, the length
+ * len must be decreased.
+ *
Each time the buffer length is decreased, it must be checked to make
+ * sure it is >0.
+ *
error is defined as a Validity Constraint Violation and
+ * is recoverable
+ *
fatal error is defined as a Well-formedness Constraint
+ * Violation and is not recoverable
+ *
+ *
+ * @author David Crawshaw
+ * @see XML Specification
+ * @see XML Namespaces
+ */
+public abstract class XML
+{
+ /////////////////////////////////////////////////////////////////////////////////////////////
+ // XML Parser
+ /////////////////////////////////////////////////////////////////////////////////////////////
+
+ public static final int BUFFER_SIZE = 255;
+
+ /** static pool of XML.Element instances shared by all XML Parsers. */
+ private static final Queue elements = new Queue(30);
+
+ private static final char[] single_amp = new char[] { '&' };
+ private static final char[] single_apos = new char[] { '\'' };
+ private static final char[] single_gt = new char[] { '>' };
+ private static final char[] single_lt = new char[] { '<' };
+ private static final char[] single_quot = new char[] { '"' };
+
+ private int line;
+ private int col;
+
+ private Reader in;
+ private char[] buf;
+ private int off;
+ private int base; // base+off == distance into the stream
+ private int len;
+
+ private Element current;
+
+ // used in readEntity() to process a single character without creating a new array
+ private char[] singlechar = new char[1];
+
+
+ public XML() { this(BUFFER_SIZE); }
+
+ public XML(int bSize) {
+ buf = new char[bSize];
+
+ current = (Element)elements.remove(false);
+ if (current == null) current = new Element();
+ }
+
+ /** Returns the line number at the beginning of the last process call. */
+ public int getLine() { return line; }
+
+ /** Returns the column number at the beginning of the last process call. */
+ public int getCol() { return col; }
+
+ /** Returns the global file offset at the beginning of the last process call. */
+ public int getGlobalOffset() { return base + off; }
+
+ /**
+ * Parse given input and call the abstract event functions.
+ *
+ * Careful with threading, as this function is not synchronized.
+ */
+ public final void parse(Reader reader) throws IOException, Exn {
+ in = reader;
+ off = len = 0;
+ line = col = 1;
+
+ clear(); // clean up possible mid-way linked-list element
+
+ try {
+ // process the stream
+ while (true) {
+ if (!buffer(1)) {
+ if (current.qName == null) break;
+ throw new Exn("reached eof without closing <"+current.qName+"> element", Exn.WFC, getLine(), getCol());
+ }
+
+ if (buf[off] == '<') readTag();
+ readChars(current.qName != null);
+ }
+ } finally { clear(); } // clean up elements
+ }
+
+ /** remove any leftover elements from the linked list and queue them */
+ private final void clear() {
+ for (Element last = current; current.parent != null; ) {
+ current = current.parent;
+ last.clear();
+ elements.append(last);
+ }
+ current.clear();
+ }
+
+ /** reads in a tag. expects buf[off] == '<' */
+ private final void readTag() throws IOException, Exn {
+ // Start Tag '<' Name (S Attribute)* S? '>'
+ boolean starttag = true;
+
+ // End Tag '' Name S? '>'
+ boolean endtag = false;
+
+ // if (starttag & endtag) then: EmptyElemTag '<' Name (S Attribute)* S? '/>'
+
+ // Position in the name of the ':' namespace prefix
+ int prefix = -1;
+
+ int namelen = 0;
+
+ col++; off++; len--;
+ if (!buffer(1)) throw new EOFException("Unexpected EOF processing element tag");
+
+ // work out what we can from the beginning of the tag
+ char s = buf[off];
+ if (s == '!') {
+ // definitions here don't necessarily conform to xml spec (as DTDs not yet implemented)
+ col++; off++; len--;
+ if (!buffer(4)) throw new EOFException("Unexpected EOF processing '
+ readChars(false, "-->", false);
+ col += 3; off += 3; len -= 3;
+ break;
+
+ // we don't care about the following definitions
+
+ case 'A':
+ if (!buffer(7)
+ || buf[off+1] != 'T' || buf[off+2] != 'T' || buf[off+3] != 'L'
+ || buf[off+4] != 'I' || buf[off+5] != 'S' || buf[off+6] != 'T') {
+ bad = true; break;
+ }
+ col += 7; off += 7; len -= 7;
+
+ // ATTLIST '') '>'
+ readChars(false, ">", true);
+ col++; off++; len--;
+ break;
+ case 'D':
+ if (!buffer(7)
+ || buf[off+1] != 'O' || buf[off+2] != 'C' || buf[off+3] != 'T'
+ || buf[off+4] != 'Y' || buf[off+5] != 'P' || buf[off+6] != 'E') {
+ bad = true; break;
+ }
+ col += 7; off += 7; len -= 7;
+
+ // DTD '') '>'
+ readChars(false, ">", true);
+ col++; off++; len--;
+ break;
+ case 'E':
+ if (!buffer(7)) {
+ bad = true;
+ } else if (buf[off+1] == 'L' && buf[off+2] == 'E' && buf[off+3] == 'M'
+ && buf[off+4] == 'E' && buf[off+5] == 'N' && buf[off+6] == 'T') {
+ // ELEMENT '') '>'
+ readChars(false, ">", true);
+ col++; off++; len--;
+
+ } else if (buf[off+1] == 'N' && buf[off+2] == 'T' && buf[off+3] == 'I'
+ && buf[off+4] == 'T' && buf[off+5] == 'Y') {
+ // ENTITY '') '>'
+ readChars(false, ">", true);
+ col++; off++; len--;
+
+ } else {
+ bad = true;
+ }
+ break;
+
+ case 'N':
+ if (!buffer(8)
+ || buf[off+1] != 'O' || buf[off+2] != 'T' || buf[off+3] != 'A' || buf[off+4] != 'T'
+ || buf[off+5] != 'I' || buf[off+6] != 'O' || buf[off+7] != 'N') {
+ bad = true; break;
+ }
+ col += 8; off += 8; len -= 8;
+ // NOTATION '') '>'
+ readChars(false, ">", true);
+ col++; off++; len--;
+
+ break;
+ default: bad = true;
+ }
+
+ if (bad) throw new Exn("element tag start character is invalid", Exn.MARKUP, getLine(), getCol());
+
+ } else if (s == '?') {
+ // PI (Ignored) '' (Char* - (Char* '?>' Char*)) '?>'
+ col++; off++; len--;
+ readChars(false, "?>", true);
+ if (!buffer(2)) throw new EOFException("Unexpected EOF at end of Processing Instruction");
+ col += 2; off += 2; len -= 2;
+
+ } else if (s == '[') {
+ if (!buffer(7)
+ || buf[off+1] != 'C' || buf[off+2] != 'D' || buf[off+3] != 'A'
+ || buf[off+4] != 'T' || buf[off+5] != 'A' || buf[off+6] != '[') {
+ col++; off--; len++;
+ // Conditional '' Char*)) ']]>'
+ readChars(false, "]]>", false);
+ } else {
+ col += 7; off += 7; len -=7;
+ // CDATA '' Char*)) ']]>'
+ readChars(true, "]]>", false);
+ }
+ col += 3; off += 3; len -= 3;
+ } else {
+ if (s == '/') {
+ // End Tag '' Name S? '>'
+ starttag = false;
+ endtag = true;
+
+ col++; off++; len--;
+ if (!buffer(1)) throw new EOFException("Unexpected EOF processing end tag");
+ s = buf[off];
+ }
+
+ if (!Name(s)) throw new Exn("invalid starting character in element name", Exn.MARKUP, getLine(), getCol());
+
+ // find the element name (defined in XML Spec: section 2.3)
+ for (namelen = 0; ; namelen++) {
+ if (!buffer(namelen+1)) throw new EOFException("Unexpected EOF in element tag name");
+
+ s = buf[off+namelen];
+
+ if (S(s) || s == '>') {
+ break;
+ } else if (s == '/') {
+ endtag = true;
+ break;
+ } else if (s == ':' && namelen > 0 && prefix < 1) {
+ // we have a definition of the prefix range available
+ prefix = namelen;
+ } else if (!NameChar(s)) {
+ throw new Exn("element name contains invalid character", Exn.MARKUP, getLine(), getCol());
+ }
+ }
+
+ // process name (based on calculated region)
+ if (namelen < 1) throw new Exn("element name is null", Exn.MARKUP, getLine(), getCol());
+
+ // we have marked out the name region, so turn it into a string and move on
+ String qName = new String(buf, off, namelen);
+
+ col += namelen; off += namelen; len -= namelen;
+
+ if (starttag) {
+ // create the in-memory element representation of this beast
+ // if current.qName == null then this is the root element we're dealing with
+ if (current.qName != null) {
+ Element next = (Element)elements.remove(false);
+ if (next == null) next = new Element();
+ //next.clear(); // TODO: remove as elements now checked as they're added to the queue
+ next.parent = current;
+ current = next;
+ }
+
+ current.qName = qName;
+
+ if (prefix > 0) {
+ current.prefix = current.qName.substring(0, prefix);
+ current.localName = current.qName.substring(prefix+1);
+ } else {
+ current.prefix = null;
+ current.localName = current.qName;
+ }
+
+ // process attributes
+ readWhitespace();
+ if (!buffer(1)) throw new EOFException("Unexpected EOF - processing attributes part 1");
+ while (buf[off] != '/' && buf[off] != '>') {
+ readAttribute();
+ if (!buffer(1)) throw new EOFException("Unexpected EOF - processing attributes part 2");
+ readWhitespace();
+ }
+
+ // work out the uri of this element
+ current.uri = current.getUri(current.getPrefix());
+ if (current.getUri().equals("") && current.getPrefix() != null)
+ current.addError(new Exn("undefined prefix '"+current.getPrefix()+"'", Exn.NC, getLine(), getCol()));
+
+ } else {
+ // this is an end-of-element tag
+ if (!qName.equals(current.getQName())) throw new Exn(
+ "end tag "+qName+"> does not line up with start tag <"+current.getQName()+">", Exn.WFC, getLine(), getCol()
+ );
+ }
+
+ // deal with whitespace
+ readWhitespace();
+
+ // process tag close
+ if (!buffer(1)) throw new EOFException("Unexpected EOF before end of tag");
+ if (buf[off] == '/') {
+ endtag = true;
+ off++; len--; col++;
+ }
+ if (!buffer(1)) throw new EOFException("Unexpected EOF before end of endtag");
+ if (buf[off] == '>') {
+ off++; len--; col++;
+ } else {
+ throw new Exn("missing '>' character from element '"+qName+"'", Exn.MARKUP, getLine(), getCol());
+ }
+
+ // send element signals
+ if (starttag) startElement(current);
+ if (endtag) {
+ endElement(current);
+
+ // we just closed an element, so remove it from the element 'stack'
+ if (current.getParent() == null) {
+ // we just finished the root element
+ current.clear();
+ } else {
+ Element last = current;
+ current = current.parent;
+ last.clear();
+ elements.append(last);
+ }
+ }
+ }
+ }
+
+ /** reads in an attribute of an element. expects Name(buf[off]) */
+ private final void readAttribute() throws IOException, Exn {
+ int ref = 0;
+ int prefix = 0;
+ String n, v, p, u; // attribute name, value, prefix and uri respectively
+ n = v = p = u = null;
+ char s;
+
+ // find the element name (defined in XML Spec: section 2.3)
+ for (ref= 0; ; ref++) {
+ if (!buffer(ref+1)) throw new EOFException("Unexpected EOF in read attribute loop part 1");
+
+ s = buf[off+ref];
+
+ if (s == '=' || S(s)) {
+ break;
+ } else if (s == ':' && ref > 0 && prefix < 1) {
+ // we have a definition of the prefix range available
+ prefix = ref+1;
+ } else if (!NameChar(s)) {
+ throw new Exn("attribute name contains invalid characters", Exn.MARKUP, getLine(), getCol());
+ }
+ }
+
+ // determine prefix and key name
+ if (prefix > 0) {
+ p = new String(buf, off, prefix-1);
+ col += prefix; off += prefix; len -= prefix; ref -= prefix;
+ }
+ n = new String(buf, off, ref);
+ col += ref; off += ref; len -= ref;
+
+ // find name/value divider ('=')
+ readWhitespace();
+ if (!buffer(1)) throw new EOFException("Unexpected EOF before attribute '=' divider");
+ if (buf[off] != '=') throw new Exn("attribute name not followed by '=' sign", Exn.MARKUP, getLine(), getCol());
+
+ col++; off++; len--;
+ readWhitespace();
+
+ if (!buffer(1)) throw new EOFException("Unexpected EOF after attribute '=' divider");
+
+ char wrap;
+ if (buf[off] == '\'' || buf[off] == '"') {
+ wrap = buf[off];
+ } else {
+ throw new Exn("attribute '"+n+"' must have attribute wrapped in ' or \"", Exn.MARKUP, getLine(), getCol());
+ }
+ col++; off++; len--;
+
+ // find the attribute value
+ attval: for (ref = 0; ; ref++) {
+ if (!buffer(ref+1)) throw new EOFException("Unexpected EOF in attribute value");
+
+ if (buf[off+ref] == wrap) {
+ break attval;
+ } else if (buf[off+ref] == '<') {
+ throw new Exn("attribute value for '"+n+"' must not contain '<'", Exn.WFC, getLine(), getCol());
+ }
+ }
+
+ v = new String(buf, off, ref);
+ col += ref; off += ref; len -= ref;
+
+ // remove end wrapper character
+ col++; off++; len--;
+
+ // process attribute
+ if (p != null && p.equals("xmlns")) {
+ current.addUri(n, v);
+ } else if (n.equals("xmlns")) {
+ if (current.getUri().equals("")) {
+ current.addUri("", v);
+ } else {
+ current.addError(new Exn("default namespace definition repeated", Exn.NC, getLine(), getCol()));
+ }
+ } else {
+ // find attribute uri
+ u = current.getUri(p);
+ if (p != null && u.equals("")) current.addError(new Exn("undefined attribute prefix '"+p+"'", Exn.NC, getLine(), getCol()));
+
+ // check to see if attribute is a repeat
+ for (int i=0; current.len > i; i++) if (n.equals(current.getAttrKey(i)) && u.equals(current.getAttrUri(i))) throw new Exn(
+ "attribute name '"+n+"' may not appear more than once in the same element tag", Exn.WFC, getLine(), getCol()
+ );
+
+ current.addAttr(n, v, u);
+ }
+ }
+
+ /** reads an entity and processes out its value. expects buf[off] == '&' */
+ private final void readEntity() throws IOException, Exn {
+ off++; len--;
+ if (!buffer(2)) throw new EOFException("Unexpected EOF reading entity");
+
+ boolean unknown = false;
+ switch (buf[off]) {
+ case '#':
+ off++; len--;
+
+ int radix;
+ if (buf[off] == 'x') { off++; len--; radix = 16; } else { radix = 10; }
+ int c = 0;
+
+ // read in each char, then shift total value to the left and add the extra
+ // style of loop is slightly different from all the others, as this should run a limited number of times
+ findchar: while (true) {
+ if (!buffer(1)) throw new EOFException("Unexpected EOF reading entity");
+ int d = Character.digit(buf[off], radix);
+ if (d == -1) {
+ if (buf[off] != ';') throw new Exn("illegal characters in entity reference", Exn.WFC, getLine(), getCol());
+ off++; len--; col++;
+ break findchar;
+ }
+ c = (c * radix) + d;
+
+ off++; len--;
+ }
+
+ singlechar[0] = Character.forDigit(c, radix);
+ characters(singlechar, 0, 1);
+ break;
+
+ case 'a':
+ if (buffer(4) && buf[off+1] == 'm' && buf[off+2] == 'p' && buf[off+3] == ';') {
+ characters(single_amp, 0, 1); // &
+ off += 4; len -= 4; col++;
+ } else if (buffer(5) && buf[off+1] == 'p' && buf[off+2] == 'o' && buf[off+3] == 's' && buf[off+4] == ';') {
+ characters(single_apos, 0, 1); // '
+ off += 5; len -= 5; col++;
+ } else {
+ unknown = true;
+ }
+ break;
+
+ case 'g':
+ if (buffer(3) && buf[off+1] == 't' && buf[off+2] == ';') {
+ characters(single_gt, 0, 1); // >
+ off += 3; len -= 3; col++;
+ } else {
+ unknown = true;
+ }
+ break;
+
+ case 'l':
+ if (buffer(3) && buf[off+1] == 't' && buf[off+2] == ';') {
+ characters(single_lt, 0, 1); // <
+ off += 3; len -= 3; col++;
+ } else {
+ unknown = true;
+ }
+ break;
+
+ case 'q':
+ if (buffer(5) && buf[off+1] == 'u' && buf[off+2] == 'o' && buf[off+3] == 't' && buf[off+4] == ';') {
+ characters(single_quot, 0, 1); // "
+ off += 5; len -= 5; col++;
+ } else {
+ unknown = true;
+ }
+ break;
+
+ // TODO: check a parser-level Hash of defined entities
+ }
+
+ if (unknown) throw new Exn("unknown entity ( not supported)", Exn.WFC, getLine(), getCol());
+ }
+
+ /** reads until the passed string is encountered. */
+ private final void readChars(boolean p, String match, boolean entities) throws IOException, Exn {
+ int ref;
+ char[] end = match.toCharArray();
+
+ for (boolean more = true; more;) {
+ if (!buffer(1)) return;
+
+ buf: for (ref = 0; ref < len; ref++) {
+ switch (buf[off+ref]) {
+ case '\r': // windows or macos9 newline
+ // normalise and process
+ buf[off+ref] = '\n'; ref++;
+ if (p) characters(buf, off, ref);
+ off += ref; len -= ref; ref = -1;
+ line++; col = 1;
+
+ // windows double-char newline; skip the next char
+ if (!buffer(1)) return;
+ if (buf[off] == '\n') { off++; len--; }
+ break;
+
+ case '\n': // unix newline
+ ref++;
+ if (p) characters(buf, off, ref);
+ off += ref; len -= ref; ref = -1;
+ line++; col = 1;
+ break;
+
+ case '&': // entity
+ if (entities) {
+ if (p) {
+ if (ref > 0) characters(buf, off, ref);
+ off += ref; len -= ref; ref = -1;
+ readEntity();
+ }
+ break;
+ }
+
+ default:
+ if (!buffer(ref+end.length)) continue buf;
+ for (int i=0; end.length > i; i++) if (end[i] != buf[off+ref+i]) continue buf;
+ more = false;
+ break buf;
+ }
+ }
+
+ if (p && ref > 0) characters(buf, off, ref);
+ off += ref; len -= ref; col += ref;
+ }
+ }
+
+ /**
+ * reads until a < symbol is encountered
+ * @param p If true call the characters(char[],int,int) funciton for the processed characters
+ */
+ private final void readChars(boolean p) throws IOException, Exn {
+ int ref;
+
+ for (boolean more = true; more;) {
+ if (!buffer(1)) return;
+
+ buf: for (ref = 0; ref < len; ref++) {
+ switch (buf[off+ref]) {
+ case '\r': // windows or macos9 newline
+ // normalise and process
+ buf[off+ref] = '\n'; ref++;
+ if (p) characters(buf, off, ref);
+ off += ref; len -= ref; ref = -1;
+ line++; col = 1;
+
+ // windows double-char newline; skip the next char
+ if (!buffer(1)) return;
+ if (buf[off] == '\n') { off++; len--; }
+ break;
+
+ case '\n': // unix newline
+ ref++;
+ if (p) characters(buf, off, ref);
+ off += ref; len -= ref; ref = -1;
+ line++; col = 1;
+ break;
+
+ case '&': // entity
+ if (p) {
+ if (ref > 0) characters(buf, off, ref);
+ off += ref; len -= ref; ref = -1;
+ readEntity();
+ }
+ break;
+
+ case '<': // end of chars section
+ more = false;
+ break buf;
+ }
+ }
+
+ if (p && ref > 0) characters(buf, off, ref);
+ off += ref; len -= ref; col += ref;
+ }
+ }
+
+ /** reads until a non-whitespace symbol is encountered */
+ private final void readWhitespace() throws IOException, Exn {
+ int ref;
+
+ for (boolean more = true; more;) {
+ if (!buffer(1)) return;
+
+ buf: for (ref = 0; ref < len; ref++) {
+ switch (buf[off+ref]) {
+ case '\r': // windows or macos9 newline
+ // normalise and process
+ buf[off+ref] = '\n';
+ whitespace(buf, off, ++ref);
+ off += ref; len -= ref; ref = -1;
+ line++; col = 1;
+
+ // windows double-char newline; skip the next char
+ if (!buffer(1)) return;
+ if (buf[off] == '\n') { off++; len--; }
+ break;
+
+ case '\n': // unix newline
+ whitespace(buf, off, ++ref);
+ off += ref; len -= ref; ref = -1;
+ line++; col = 1;
+ break;
+
+ case ' ': // space
+ case '\t': // tab
+ break;
+
+ default: // end of whitespace
+ more = false;
+ break buf;
+ }
+ }
+
+ off += ref; len -= ref; col += ref;
+ }
+ }
+
+ /**
+ * attempt to fill the buffer.
+ *
+ * @param min Minimum number of characters to read (even if we have to block to do it).
+ * @return return false if min can't be reached.
+ */
+ private final boolean buffer(int min) throws IOException {
+ if (len > min) return true;
+
+ if (buf.length - (off+len) >= min) {
+ // plenty of space left on the end of the buffer
+ } else if (off >= min) {
+ // moving offset data to start will leave enough free space on the end
+ System.arraycopy(buf, off, buf, 0, len);
+ base += off;
+ off = 0;
+ } else {
+ // buffer size will have to be increased
+ char[] newbuf = new char[buf.length * 2];
+ System.arraycopy(buf, off, newbuf, 0, len);
+ buf = newbuf;
+ base += off;
+ off = 0;
+ }
+
+ while (min > len) {
+ int newlen = in.read(buf, off+len, buf.length-(off+len));
+ if (newlen < 0) return false;
+ len += newlen;
+ }
+
+ return true;
+ }
+
+
+ /////////////////////////////////////////////////////////////////////////////////////////////
+ // Abstract SAX-Like Interface
+ /////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Called when the start of an element is processed.
+ *
+ *
DO NOT store a reference to the Element object, as
+ * they are reused by XML Parser.
+ */
+ public abstract void startElement(Element e) throws Exn;
+
+ /**
+ * Represents up to a line of character data.
+ *
+ *
Newlines are all normalised to the Unix \n as per the XML Spec,
+ * and a newline will only appear as the last character in the passed
+ * array segment.
+ *
+ *
XML.getLine() and XML.getCol() report the position at the
+ * beginning of this character segment, which can be processed in a
+ * line-by-line fashion due to the above newline restriction.
+ */
+ public abstract void characters(char[] ch, int start, int length) throws Exn, IOException;
+
+ /** Represents up to a line of ignorable whitespace. */
+ public abstract void whitespace(char[] ch, int start, int length) throws Exn, IOException;
+
+ /** Represents the end of an Element. */
+ public abstract void endElement(Element e) throws Exn, IOException;
+
+
+ /////////////////////////////////////////////////////////////////////////////////////////////
+ // Inner Classes for Parser Support
+ /////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Represents an element in an XML document. Stores a reference to its
+ * parent, forming a one-way linked list.
+ *
+ * Element objects are reused, so client code making use of them must
+ * drop their references after the specific element process function
+ * has returned.
+ */
+ public static final class Element {
+
+ private static final int DEFAULT_ATTR_SIZE = 10;
+
+ protected Element parent = null;
+
+ protected String uri = null;
+ protected String localName = null;
+ protected String qName = null;
+ protected String prefix = null;
+
+ protected Hash urimap = new Hash(3,3);
+
+ protected String[] keys = new String[DEFAULT_ATTR_SIZE];
+ protected String[] vals = new String[DEFAULT_ATTR_SIZE];
+ protected String[] uris = new String[DEFAULT_ATTR_SIZE];
+ protected int len = 0;
+
+ protected Exn[] errors = new Exn[] {};
+
+ /** Parent of current element. */
+ public Element getParent() { return parent; }
+
+ /** Qualified Name of current element. XML Namespace Spec 14-Jan-1999 [6] */
+ public String getQName() { return qName; }
+
+ /** LocalPart of current element. XML Namespace Spec 14-Jan-1999 [8] */
+ public String getLocalName() { return localName; }
+
+ /** Prefix of current element. Substring of qName. XML Namespace Spec 14-Jan-1999 [7] */
+ public String getPrefix() { return prefix; }
+
+ // HACK
+ public Hash getUriMap() {
+ Hash map = new Hash();
+ for (Element e = this; e != null; e = e.getParent()) {
+ java.util.Enumeration en = e.urimap.keys();
+ while(en.hasMoreElements()) {
+ String key = (String)en.nextElement();
+ String val = getUri(key);
+ map.put(key, val);
+ }
+ }
+ return map;
+ }
+
+ /** URI of current tag. XML Namespace Spec 14-Jan-1999 section 1 */
+ public String getUri() { return getUri(prefix); }
+
+ /** URI of a given prefix. Never returns null, instead gives "". */
+ public String getUri(String p) {
+ String ret = null;
+ for (Element e = this; e != null && ret == null; e = e.getParent()) {
+ ret = (String)e.urimap.get(p == null ? "" : p);
+ }
+ return ret == null ? "" : ret;
+ }
+
+ /** An array of attribute names. */
+ public String getAttrKey(int pos) { return len > pos ? keys[pos] : null; }
+
+ /** An array of attribute values. */
+ public String getAttrVal(int pos) { return len > pos ? vals[pos] : null; }
+
+ /** An array of attribute uris. */
+ public String getAttrUri(int pos) { return len > pos ? uris[pos] : null; }
+
+ /** Current number of attributes in the element. */
+ public int getAttrLen() { return len; }
+
+ /** Poor performance, but easier to use when speed is not a concern */
+ public Hash getAttrHash() {
+ Hash ret = new Hash(getAttrLen() * 2, 3);
+ for(int i=0; i i; i++) { keys[i] = null; vals[i] = null; uris[i] = null; };
+ }
+ len = 0;
+
+ errors = new Exn[] {};
+ }
+ }
+
+ /** Parse or Structural Error */
+ public static class Exn extends Exception {
+ /** Violation of Markup restrictions in XML Specification - Fatal Error */
+ public static final int MARKUP = 1;
+
+ /** Well-Formedness Constraint Violation - Fatal Error */
+ public static final int WFC = 2;
+
+ /** Namespace Constraint Violation - Recoverable Error */
+ public static final int NC = 3;
+
+ /** Schema Violation - Fatal Error */
+ public static final int SCHEMA = 4;
+
+ private String error;
+ private int type;
+ private int line;
+ private int col;
+
+ public Exn(String e) { this(e, MARKUP, -1, -1); }
+
+ public Exn(String e, int type, int line, int col) {
+ this.error = e;
+ this.type = type;
+ this.line = line;
+ this.col = col;
+ }
+
+ public int getType() { return this.type; }
+ public int getLine() { return this.line; }
+ public int getCol() { return this.col; }
+ public String getMessage() { return this.error + (line >= 0 && col >= 0 ? " at " + line + ":" + col: ""); }
+ }
+
+
+ /////////////////////////////////////////////////////////////////////////////////////////////
+ // Static Support Functions for the XML Specification
+ /////////////////////////////////////////////////////////////////////////////////////////////
+
+ // attempt to avoid these functions unless you *expect* the input to fall in the given range.
+
+ /** First Character of Name - XML Specification 1.0 [5] */
+ private static final boolean Name(char c) {
+ return BaseCharAscii(c) || c == '_' || c == ':' || Letter(c);
+ }
+
+ /** NameChar - XML Specification 1.0 [4] */
+ private static final boolean NameChar(char c) {
+ return BaseCharAscii(c) || c == '.' || c == '-' || c == '_' || c == ':'
+ || Digit(c) || Letter(c) || Extender(c); // TODO: || CombiningChar(c);
+ }
+
+ /** BaseChar - XMl Specification 1.0 [84] */
+ private static final boolean Letter(char c) {
+ return BaseChar(c) || Ideographic(c);
+ }
+
+ /** Elements of BaseChar that exist in ASCII. */
+ private static final boolean BaseCharAscii(char c) {
+ return (c >= '\u0041' && c <= '\u005A') || (c >= '\u0061' && c <= '\u007A');
+ }
+
+ /** Char - XML Specification 1.0 [2] */
+ private static final boolean Char(char c) {
+ // u000A == r and u000D == n, but the javac compiler can't handle the \ u form
+ return c == '\u0009' || c == '\r' || c == '\n'
+ || (c >= '\u0020' && c <= '\uD7FF')
+ || (c >= '\uE000' && c <= '\uFFFD');
+ }
+
+ /** BaseChar - XML Specification 1.0 [85] */
+ private static final boolean BaseChar(char c) {
+ return BaseCharAscii(c) || (c >= '\u00C0' && c <= '\u00D6')
+ || (c >= '\u00D8' && c <= '\u00F6') || (c >= '\u00F8' && c <= '\u00FF') || (c >= '\u0100' && c <= '\u0131')
+ || (c >= '\u0134' && c <= '\u013E') || (c >= '\u0141' && c <= '\u0148') || (c >= '\u014A' && c <= '\u017E')
+ || (c >= '\u0180' && c <= '\u01C3') || (c >= '\u01CD' && c <= '\u01F0') || (c >= '\u01F4' && c <= '\u01F5')
+ || (c >= '\u01FA' && c <= '\u0217') || (c >= '\u0250' && c <= '\u02A8') || (c >= '\u02BB' && c <= '\u02C1')
+ || (c == '\u0386') || (c >= '\u0388' && c <= '\u038A') || (c == '\u038C')
+ || (c >= '\u038E' && c <= '\u03A1') || (c >= '\u03A3' && c <= '\u03CE') || (c >= '\u03D0' && c <= '\u03D6')
+ || (c == '\u03DA') || (c == '\u03DC') || (c == '\u03DE')
+ || (c == '\u03E0')
+ || (c >= '\u03E2' && c <= '\u03F3') || (c >= '\u0401' && c <= '\u040C') || (c >= '\u040E' && c <= '\u044F')
+ || (c >= '\u0451' && c <= '\u045C') || (c >= '\u045E' && c <= '\u0481') || (c >= '\u0490' && c <= '\u04C4')
+ || (c >= '\u04C7' && c <= '\u04C8') || (c >= '\u04CB' && c <= '\u04CC') || (c >= '\u04D0' && c <= '\u04EB')
+ || (c >= '\u04EE' && c <= '\u04F5') || (c >= '\u04F8' && c <= '\u04F9') || (c >= '\u0531' && c <= '\u0556')
+ || (c == '\u0559')
+ || (c >= '\u0561' && c <= '\u0586') || (c >= '\u05D0' && c <= '\u05EA') || (c >= '\u05F0' && c <= '\u05F2')
+ || (c >= '\u0621' && c <= '\u063A') || (c >= '\u0641' && c <= '\u064A') || (c >= '\u0671' && c <= '\u06B7')
+ || (c >= '\u06BA' && c <= '\u06BE') || (c >= '\u06C0' && c <= '\u06CE') || (c >= '\u06D0' && c <= '\u06D3')
+ || (c == '\u06D5')
+ || (c >= '\u06E5' && c <= '\u06E6') || (c >= '\u0905' && c <= '\u0939')
+ || (c == '\u093D')
+ || (c >= '\u0958' && c <= '\u0961') || (c >= '\u0985' && c <= '\u098C') || (c >= '\u098F' && c <= '\u0990')
+ || (c >= '\u0993' && c <= '\u09A8') || (c >= '\u09AA' && c <= '\u09B0')
+ || (c == '\u09B2')
+ || (c >= '\u09B6' && c <= '\u09B9') || (c >= '\u09DF' && c <= '\u09E1') || (c >= '\u09F0' && c <= '\u09F1')
+ || (c >= '\u0A05' && c <= '\u0A0A') || (c >= '\u0A0F' && c <= '\u0A10') || (c >= '\u0A13' && c <= '\u0A28')
+ || (c >= '\u0A2A' && c <= '\u0A30') || (c >= '\u0A32' && c <= '\u0A33') || (c >= '\u0A35' && c <= '\u0A36')
+ || (c >= '\u0A38' && c <= '\u0A39') || (c >= '\u0A59' && c <= '\u0A5C')
+ || (c == '\u0A5E')
+ || (c >= '\u0A72' && c <= '\u0A74') || (c >= '\u0A85' && c <= '\u0A8B')
+ || (c == '\u0A8D')
+ || (c >= '\u0A8F' && c <= '\u0A91') || (c >= '\u0A93' && c <= '\u0AA8') || (c >= '\u0AAA' && c <= '\u0AB0')
+ || (c >= '\u0AB2' && c <= '\u0AB3') || (c >= '\u0AB5' && c <= '\u0AB9')
+ || (c == '\u0ABD')
+ || (c == '\u0AE0')
+ || (c >= '\u0B05' && c <= '\u0B0C') || (c >= '\u0B0F' && c <= '\u0B10') || (c >= '\u0B13' && c <= '\u0B28')
+ || (c >= '\u0B2A' && c <= '\u0B30') || (c >= '\u0B32' && c <= '\u0B33') || (c >= '\u0B36' && c <= '\u0B39')
+ || (c == '\u0B3D')
+ || (c >= '\u0B5C' && c <= '\u0B5D') || (c >= '\u0B5F' && c <= '\u0B61') || (c >= '\u0B85' && c <= '\u0B8A')
+ || (c >= '\u0B8E' && c <= '\u0B90') || (c >= '\u0B92' && c <= '\u0B95') || (c >= '\u0B99' && c <= '\u0B9A')
+ || (c == '\u0B9C')
+ || (c >= '\u0B9E' && c <= '\u0B9F') || (c >= '\u0BA3' && c <= '\u0BA4') || (c >= '\u0BA8' && c <= '\u0BAA')
+ || (c >= '\u0BAE' && c <= '\u0BB5') || (c >= '\u0BB7' && c <= '\u0BB9') || (c >= '\u0C05' && c <= '\u0C0C')
+ || (c >= '\u0C0E' && c <= '\u0C10') || (c >= '\u0C12' && c <= '\u0C28') || (c >= '\u0C2A' && c <= '\u0C33')
+ || (c >= '\u0C35' && c <= '\u0C39') || (c >= '\u0C60' && c <= '\u0C61') || (c >= '\u0C85' && c <= '\u0C8C')
+ || (c >= '\u0C8E' && c <= '\u0C90') || (c >= '\u0C92' && c <= '\u0CA8') || (c >= '\u0CAA' && c <= '\u0CB3')
+ || (c >= '\u0CB5' && c <= '\u0CB9')
+ || (c == '\u0CDE')
+ || (c >= '\u0CE0' && c <= '\u0CE1') || (c >= '\u0D05' && c <= '\u0D0C') || (c >= '\u0D0E' && c <= '\u0D10')
+ || (c >= '\u0D12' && c <= '\u0D28') || (c >= '\u0D2A' && c <= '\u0D39') || (c >= '\u0D60' && c <= '\u0D61')
+ || (c >= '\u0E01' && c <= '\u0E2E')
+ || (c == '\u0E30')
+ || (c >= '\u0E32' && c <= '\u0E33') || (c >= '\u0E40' && c <= '\u0E45') || (c >= '\u0E81' && c <= '\u0E82')
+ || (c == '\u0E84')
+ || (c >= '\u0E87' && c <= '\u0E88')
+ || (c == '\u0E8A')
+ || (c == '\u0E8D')
+ || (c >= '\u0E94' && c <= '\u0E97') || (c >= '\u0E99' && c <= '\u0E9F') || (c >= '\u0EA1' && c <= '\u0EA3')
+ || (c == '\u0EA5')
+ || (c == '\u0EA7')
+ || (c >= '\u0EAA' && c <= '\u0EAB') || (c >= '\u0EAD' && c <= '\u0EAE')
+ || (c == '\u0EB0')
+ || (c >= '\u0EB2' && c <= '\u0EB3')
+ || (c == '\u0EBD')
+ || (c >= '\u0EC0' && c <= '\u0EC4') || (c >= '\u0F40' && c <= '\u0F47') || (c >= '\u0F49' && c <= '\u0F69')
+ || (c >= '\u10A0' && c <= '\u10C5') || (c >= '\u10D0' && c <= '\u10F6')
+ || (c == '\u1100')
+ || (c >= '\u1102' && c <= '\u1103') || (c >= '\u1105' && c <= '\u1107')
+ || (c == '\u1109')
+ || (c >= '\u110B' && c <= '\u110C') || (c >= '\u110E' && c <= '\u1112')
+ || (c == '\u113C')
+ || (c == '\u113E')
+ || (c == '\u1140')
+ || (c == '\u114C')
+ || (c == '\u114E')
+ || (c == '\u1150')
+ || (c >= '\u1154' && c <= '\u1155')
+ || (c == '\u1159')
+ || (c >= '\u115F' && c <= '\u1161')
+ || (c == '\u1163')
+ || (c == '\u1165')
+ || (c == '\u1167')
+ || (c == '\u1169')
+ || (c >= '\u116D' && c <= '\u116E') || (c >= '\u1172' && c <= '\u1173')
+ || (c == '\u1175')
+ || (c == '\u119E')
+ || (c == '\u11A8')
+ || (c == '\u11AB')
+ || (c >= '\u11AE' && c <= '\u11AF') || (c >= '\u11B7' && c <= '\u11B8')
+ || (c == '\u11BA')
+ || (c >= '\u11BC' && c <= '\u11C2')
+ || (c == '\u11EB')
+ || (c == '\u11F0')
+ || (c == '\u11F9')
+ || (c >= '\u1E00' && c <= '\u1E9B') || (c >= '\u1EA0' && c <= '\u1EF9') || (c >= '\u1F00' && c <= '\u1F15')
+ || (c >= '\u1F18' && c <= '\u1F1D') || (c >= '\u1F20' && c <= '\u1F45') || (c >= '\u1F48' && c <= '\u1F4D')
+ || (c >= '\u1F50' && c <= '\u1F57')
+ || (c == '\u1F59')
+ || (c == '\u1F5B')
+ || (c == '\u1F5D')
+ || (c >= '\u1F5F' && c <= '\u1F7D') || (c >= '\u1F80' && c <= '\u1FB4') || (c >= '\u1FB6' && c <= '\u1FBC')
+ || (c == '\u1FBE')
+ || (c >= '\u1FC2' && c <= '\u1FC4') || (c >= '\u1FC6' && c <= '\u1FCC') || (c >= '\u1FD0' && c <= '\u1FD3')
+ || (c >= '\u1FD6' && c <= '\u1FDB') || (c >= '\u1FE0' && c <= '\u1FEC') || (c >= '\u1FF2' && c <= '\u1FF4')
+ || (c >= '\u1FF6' && c <= '\u1FFC')
+ || (c == '\u2126')
+ || (c >= '\u212A' && c <= '\u212B')
+ || (c == '\u212E')
+ || (c >= '\u2180' && c <= '\u2182') || (c >= '\u3041' && c <= '\u3094') || (c >= '\u30A1' && c <= '\u30FA')
+ || (c >= '\u3105' && c <= '\u312C') || (c >= '\uAC00' && c <= '\uD7A3');
+ }
+
+ /** BaseChar - XMl Specification 1.0 [86] */
+ private static final boolean Ideographic(char c) {
+ return (c >= '\u4E00' && c <= '\u9FA5') || c == '\u3007' || (c >= '\u3021' && c <= '\u3029');
+ }
+
+ /** CombiningChar - XMl Specification 1.0 [87] */
+ /*private static final boolean CombiningChar(char c) {
+ return (c >= '\u0300' && c <= '\u0345')
+ || (c >= '\u0360' && c <= '\u0361') || (c >= '\u0483' && c <= '\u0486') || (c >= '\u0591' && c <= '\u05A1')
+ || (c >= '\u05A3' && c <= '\u05B9') || (c >= '\u05BB' && c <= '\u05BD')
+ || (c == '\u05BF')
+ || (c >= '\u05C1' && c <= '\u05C2')
+ || (c == '\u05C4')
+ || (c >= '\u064B' && c <= '\u0652')
+ || (c == '\u0670')
+ || (c >= '\u06D6' && c <= '\u06DC') || (c >= '\u06DD' && c <= '\u06DF') || (c >= '\u06E0' && c <= '\u06E4')
+ || (c >= '\u06E7' && c <= '\u06E8') || (c >= '\u06EA' && c <= '\u06ED') || (c >= '\u0901' && c <= '\u0903')
+ || (c == '\u093C')
+ || (c >= '\u093E' && c <= '\u094C')
+ || (c == '\u094D')
+ || (c >= '\u0951' && c <= '\u0954') || (c >= '\u0962' && c <= '\u0963') || (c >= '\u0981' && c <= '\u0983')
+ || (c == '\u09BC')
+ || (c == '\u09BE')
+ || (c == '\u09BF')
+ || (c >= '\u09C0' && c <= '\u09C4') || (c >= '\u09C7' && c <= '\u09C8') || (c >= '\u09CB' && c <= '\u09CD')
+ || (c == '\u09D7')
+ || (c >= '\u09E2' && c <= '\u09E3')
+ || (c == '\u0A02')
+ || (c == '\u0A3C')
+ || (c == '\u0A3E')
+ || (c == '\u0A3F')
+ || (c >= '\u0A40' && c <= '\u0A42') || (c >= '\u0A47' && c <= '\u0A48') || (c >= '\u0A4B' && c <= '\u0A4D')
+ || (c >= '\u0A70' && c <= '\u0A71') || (c >= '\u0A81' && c <= '\u0A83')
+ || (c == '\u0ABC')
+ || (c >= '\u0ABE' && c <= '\u0AC5') || (c >= '\u0AC7' && c <= '\u0AC9') || (c >= '\u0ACB' && c <= '\u0ACD')
+ || (c >= '\u0B01' && c <= '\u0B03')
+ || (c == '\u0B3C')
+ || (c >= '\u0B3E' && c <= '\u0B43') || (c >= '\u0B47' && c <= '\u0B48') || (c >= '\u0B4B' && c <= '\u0B4D')
+ || (c >= '\u0B56' && c <= '\u0B57') || (c >= '\u0B82' && c <= '\u0B83') || (c >= '\u0BBE' && c <= '\u0BC2')
+ || (c >= '\u0BC6' && c <= '\u0BC8') || (c >= '\u0BCA' && c <= '\u0BCD')
+ || (c == '\u0BD7')
+ || (c >= '\u0C01' && c <= '\u0C03') || (c >= '\u0C3E' && c <= '\u0C44') || (c >= '\u0C46' && c <= '\u0C48')
+ || (c >= '\u0C4A' && c <= '\u0C4D') || (c >= '\u0C55' && c <= '\u0C56') || (c >= '\u0C82' && c <= '\u0C83')
+ || (c >= '\u0CBE' && c <= '\u0CC4') || (c >= '\u0CC6' && c <= '\u0CC8') || (c >= '\u0CCA' && c <= '\u0CCD')
+ || (c >= '\u0CD5' && c <= '\u0CD6') || (c >= '\u0D02' && c <= '\u0D03') || (c >= '\u0D3E' && c <= '\u0D43')
+ || (c >= '\u0D46' && c <= '\u0D48') || (c >= '\u0D4A' && c <= '\u0D4D')
+ || (c == '\u0D57')
+ || (c == '\u0E31')
+ || (c >= '\u0E34' && c <= '\u0E3A') || (c >= '\u0E47' && c <= '\u0E4E')
+ || (c == '\u0EB1')
+ || (c >= '\u0EB4' && c <= '\u0EB9') || (c >= '\u0EBB' && c <= '\u0EBC') || (c >= '\u0EC8' && c <= '\u0ECD')
+ || (c >= '\u0F18' && c <= '\u0F19')
+ || (c == '\u0F35')
+ || (c == '\u0F37')
+ || (c == '\u0F39')
+ || (c == '\u0F3E')
+ || (c == '\u0F3F')
+ || (c >= '\u0F71' && c <= '\u0F84') || (c >= '\u0F86' && c <= '\u0F8B') || (c >= '\u0F90' && c <= '\u0F95')
+ || (c == '\u0F97')
+ || (c >= '\u0F99' && c <= '\u0FAD') || (c >= '\u0FB1' && c <= '\u0FB7')
+ || (c == '\u0FB9')
+ || (c >= '\u20D0' && c <= '\u20DC')
+ || (c == '\u20E1')
+ || (c >= '\u302A' && c <= '\u302F')
+ || (c == '\u3099')
+ || (c == '\u309A');
+ }*/
+
+ /** Digit - XMl Specification 1.0 [88] */
+ private static final boolean Digit(char c) {
+ return (c >= '\u0030' && c <= '\u0039') || (c >= '\u0660' && c <= '\u0669') || (c >= '\u06F0' && c <= '\u06F9')
+ || (c >= '\u0966' && c <= '\u096F') || (c >= '\u09E6' && c <= '\u09EF') || (c >= '\u0A66' && c <= '\u0A6F')
+ || (c >= '\u0AE6' && c <= '\u0AEF') || (c >= '\u0B66' && c <= '\u0B6F') || (c >= '\u0BE7' && c <= '\u0BEF')
+ || (c >= '\u0C66' && c <= '\u0C6F') || (c >= '\u0CE6' && c <= '\u0CEF') || (c >= '\u0D66' && c <= '\u0D6F')
+ || (c >= '\u0E50' && c <= '\u0E59') || (c >= '\u0ED0' && c <= '\u0ED9') || (c >= '\u0F20' && c <= '\u0F29');
+ }
+
+ /** Extender - XMl Specification 1.0 [89] */
+ private static final boolean Extender(char c) {
+ return c == '\u00B7' || c == '\u02D0' || c == '\u02D1' || c == '\u0387'
+ || c == '\u0640' || c == '\u0E46' || c == '\u0EC6' || c == '\u3005'
+ || (c >= '\u3031' && c <= '\u3035') || (c >= '\u309D' && c <= '\u309E') || (c >= '\u30FC' && c <= '\u30FE');
+ }
+
+ /** Whitespace - XML Specification 1.0 [3] */
+ private static final boolean S(char c) {
+ return c == '\u0020' || c == '\u0009' || c == '\r' || c == '\n';
+ }
+}
diff --git a/upstream/org.ibex.core/doc/ibex-doc.xml b/upstream/org.ibex.core/doc/ibex-doc.xml
new file mode 100644
index 0000000..05e49d9
--- /dev/null
+++ b/upstream/org.ibex.core/doc/ibex-doc.xml
@@ -0,0 +1,204 @@
+
+
+
+
+
+
+ Foo.
+
+
+
+
+
+
+
+ IbexDoc targets four primary documentation tasks:
+
+
+
+ Books, Manuals and references
+
+ Articles
+
+ Literate Programming (documentation embedded in source code)
+
+ Presentation Slides
+
+ Web pages, including WikiWebs
+
+
+
+
+
+ The goal of separating markup from content has been
+ around for a long time in the document world. For some reason it
+ never succeeded. The problem is that:
+
+
+ The purpose of content is to convey semantic meaning.
+
+ Some markup and formatting carries semantic meaning.
+
+
+ For example, consider italicization. Adding or removing
+ italicization can actually affect the meaning of a piece of text.
+ Therefor italicization (or rather "emphasis") is part of the
+ content of a document.
+
+ IbexDoc recognizes this and does not attempt to use XML for
+ anything that might carry semantic meaning. Effectively, you can
+ remove all XML tags from an IbexDoc file without altering the
+ meaning of the text (although it might be rather hard to discern
+ the meaning with all the text jumbled together!)
+
+
+
+
+
+
+ (title subtitle logo license options(toc,index) logo)
+
+
+
+ (email, name)
+
+
+
+ changes
+
+
+
+ (contains sections, must appear once at EOF)
+
+
+
+ (title)
+
+
+
+ ..
+
+
+
+ ..
+
+
+
+ (a different kind of list?)
+
+
+
+ ..
+
+
+
+ ..
+
+
+
+ ..
+
+
+
+ ..
+
+
+
+ ..
+
+
+
+ ..
+
+
+
+ ..
+
+
+
+ ..
+
+
+
+ ..
+
+
+
+
+
+
+
+
+ /** */
+ ///
+ ///<
+ @param
+ @throws
+ @see
+ @link, autolinking (wiki-style?) of other classes/modules
+ @return
+ @deprecated
+ @since
+ @author?
+ package-level overview (but it's lame to put it in a separate file)
+ inheritance
+
+
+
+
+
\ No newline at end of file
diff --git a/upstream/org.ibex.core/doc/reference/alignmentpoint.pdf b/upstream/org.ibex.core/doc/reference/alignmentpoint.pdf
new file mode 100644
index 0000000..82b5cd4
Binary files /dev/null and b/upstream/org.ibex.core/doc/reference/alignmentpoint.pdf differ
diff --git a/upstream/org.ibex.core/doc/reference/grid.pdf b/upstream/org.ibex.core/doc/reference/grid.pdf
new file mode 100644
index 0000000..219d379
Binary files /dev/null and b/upstream/org.ibex.core/doc/reference/grid.pdf differ
diff --git a/upstream/org.ibex.core/doc/reference/layout.pdf b/upstream/org.ibex.core/doc/reference/layout.pdf
new file mode 100644
index 0000000..853b40d
Binary files /dev/null and b/upstream/org.ibex.core/doc/reference/layout.pdf differ
diff --git a/upstream/org.ibex.core/doc/reference/lifecycle.pdf b/upstream/org.ibex.core/doc/reference/lifecycle.pdf
new file mode 100644
index 0000000..940e482
Binary files /dev/null and b/upstream/org.ibex.core/doc/reference/lifecycle.pdf differ
diff --git a/upstream/org.ibex.core/doc/reference/offscreen.pdf b/upstream/org.ibex.core/doc/reference/offscreen.pdf
new file mode 100644
index 0000000..769be64
Binary files /dev/null and b/upstream/org.ibex.core/doc/reference/offscreen.pdf differ
diff --git a/upstream/org.ibex.core/doc/reference/pdftricks.sty b/upstream/org.ibex.core/doc/reference/pdftricks.sty
new file mode 100644
index 0000000..68ae554
--- /dev/null
+++ b/upstream/org.ibex.core/doc/reference/pdftricks.sty
@@ -0,0 +1,363 @@
+%
+% pdftricks.sty
+%
+% Copyright (c) 2001, Radhakrishnan CV
+% Rajagopal CV
+% http://www.river-valley.com
+%
+% River Valley Technologies, Software Technology Park,
+% Trivandrum, India 695034
+%
+% Tel: +91 471 33 7501/7502
+%
+% Antoine Chambert-Loir
+%
+% http://www.math.polytechnique.fr/~chambert
+%
+% Ecole polytechnique, Palaiseau Cedex, France
+%
+%
+% 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; either version 2
+% of the License, or (at your option) any later version.
+%
+% 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 (gpl.txt); if not, write to the Free
+% Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+% MA 02111-1307, USA.
+%
+% $Id: pdftricks.sty,v 1.15 2001/09/30 11:21:23 cvr Exp $
+%
+\NeedsTeXFormat{LaTeX2e}
+\def\Fileversion$#1: #2 ${\gdef\fileversion{#2}}
+\def\Filedate$#1: #2 #3 ${\gdef\filedate{#2}}
+\Fileversion$Revision: 1.15 $
+\Filedate$Date: 2001/09/30 11:21:23 $
+\ProvidesPackage{pdftricks}
+ [\filedate\space\fileversion\space psTricks support in PDF (CVRACL)]
+\PackageWarningNoLine{pdftricks}
+ {****************************************\MessageBreak
+ Package pdftricks v,\fileversion\space loaded\MessageBreak
+ [psTricks support in PDF (CVR, ACL)]\MessageBreak
+ ****************************************}
+\RequirePackage{graphicx,color}
+\newif\if@debug\@debugfalse
+\newif\ifPDFTshell
+\newif\ifPDFTnopdf
+\newif\ifnoprocess \noprocessfalse
+\newif\ifmiktex \miktexfalse
+\DeclareOption{debug}{\@debugtrue}
+\DeclareOption{noshell}{\PDFTshellfalse}
+\DeclareOption{shell}{\PDFTshelltrue}
+\DeclareOption{miktex}{\global\miktextrue}
+\ExecuteOptions{shell}
+\ProcessOptions\relax
+\ifPDFTshell
+% we must set it to false if \write18 doesn't work.
+% Hack given by Thierry Bouche (Thanks !)
+\def\tmpfile{/tmp/w18-test-\the\year\the\month\the\day\the\time}
+\ifmiktex%
+ \immediate\write18{rem >"\tmpfile"}%%%%%% LDL-2
+\else
+ \immediate\write18{touch \tmpfile} %%%%%% LDL-1
+\fi
+\ifmiktex
+ \IfFileExists{\tmpfile.}{\PDFTshelltrue}{\PDFTshellfalse} %%%%%% LDL-4
+\else
+ \IfFileExists{\tmpfile}{\PDFTshelltrue}{\PDFTshellfalse} %%%%%% LDL-3
+\fi
+\fi
+\ifPDFTshell
+ \PackageWarningNoLine{pdftricks}
+ {****************************************\MessageBreak
+ Using \csname write\endcsname18 capability \MessageBreak
+ for producing PDF-figures. \MessageBreak
+ ****************************************}
+\else
+ \PackageWarningNoLine{pdftricks}
+ {****************************************\MessageBreak
+ No \csname write\endcsname18 capability.\MessageBreak
+ You'll have to run a script by yourself!\MessageBreak
+ ****************************************}
+\fi
+
+% warning! the definition of FIGURES if pst2pdf must be set accordingly !!
+\def\PDFTfigname{\jobname-fig\thepsfig}
+\def\PDFTWarning#1#2{\if@debug\PackageWarning{#1}{#2}\fi}
+\def\PDFTWarningNoLine#1#2{\if@debug\PackageWarningNoLine{#1}{#2}\fi}
+\def\makeinnocent#1{\catcode`#1=12 }
+\def\csarg#1#2{\expandafter#1\csname#2\endcsname}
+\def\latexname{lplain}\def\latexename{LaTeX2e}
+\newwrite\PDFStream
+
+\long\def\ProcessStream#1% start it all of
+ {\begingroup%
+ \def\CurrentStream{#1}%
+ \let\do\makeinnocent \dospecials
+ \makeinnocent\^^L% and whatever other special cases
+ \endlinechar`\^^M \catcode`\^^M=12 \xStream}
+{\catcode`\^^M=12 \endlinechar=-1 %
+ \gdef\xStream#1^^M{%
+ \expandafter\ProcessStreamLine}
+ \gdef\ProcessStreamLine#1^^M{\def\test{#1}
+ \csarg\ifx{End\CurrentStream Test}\test
+ \edef\next{\noexpand\EndOfStream{\CurrentStream}}%
+ \else \ThisStream{#1}\let\next\ProcessStreamLine
+ \fi \next}
+}
+\long\def\streaminfo{\string\end{document}}
+\def\CSstringmeaning#1{\expandafter\CSgobblearrow\meaning#1}
+\def\CSstringcsnoescape#1{\expandafter\CSgobbleescape\string#1}
+{\escapechar-1
+\expandafter\expandafter\expandafter\gdef
+ \expandafter\expandafter\expandafter\CSgobblearrow
+ \expandafter\string\csname macro:->\endcsname{}
+}
+\def\CSgobbleescape#1{\ifnum`\\=`#1 \else #1\fi}
+\def\WriteStreamLine#1{\def\CStmp{#1}%
+ \immediate\write\PDFStream{\CSstringmeaning\CStmp}}
+
+\def\AfterIncludedStream
+ {\immediate\closeout\PDFStream %changed on 2001/1/20
+ \relax
+ }%
+\def\BeforeIncludedStream
+ {\stepcounter{psfig}\xdef\PDFCutFile{\PDFTfigname.tex}%
+ \message{Opening PDFStream=\PDFCutFile}%
+ \immediate\openout\PDFStream=\PDFCutFile
+ \immediate\write\PDFStream{\string\documentclass{article}}
+ \immediate\write\PDFStream{\string\input\space tmp.inputs}
+ \immediate\write\PDFStream{\string\pagestyle{empty}}
+ \immediate\write\PDFStream{\string\usepackage{amssymb,amsbsy}}
+ \immediate\write\PDFStream{\string\begin{document}}
+ \let\ThisStream\WriteStreamLine}
+\long\def\specialstream #1#2#3{%
+ \message{Special stream '#1'}%
+ \csarg\def{After#1Stream}{#2\AfterIncludedStream#3}%
+ \csarg\def{#1}{\BeforeIncludedStream\relax
+ \ProcessStream{#1}}%
+ \PDFEndDef{#1}}
+\def\EndOfStream#1{\endgroup\end{#1}%
+ \csname After#1Stream\endcsname}
+\def\PDFEndDef#1{{\escapechar=-1\relax
+ \csarg\xdef{End#1Test}{\string\\end\string\{#1\string\}}%
+ }}
+%%
+%% The real meat of psfile manipulation starts here.
+%%
+%%
+\AtEndDocument{\endPShook%
+ \ifPDFTnopdf
+ \PackageWarningNoLine{pdftricks}
+ {******************************************\MessageBreak
+ Some PDF files of images were not found.\MessageBreak
+ Run the script `pst2pdf' before the next\MessageBreak
+ run of pdfLaTeX\MessageBreak
+ ******************************************}
+ \fi
+}
+\gdef\endPShook{}
+\def\noprocess{\global\noprocesstrue
+ \PackageWarning{pdftricks}
+ {******************************************\MessageBreak
+ Figure Number: \PDFTfigname\space is not processed \MessageBreak
+ ******************************************\MessageBreak}
+}
+\specialstream{pdfpic}{%
+ \immediate\write\PDFStream{\streaminfo}}
+ {\psgraphicsinclude\global\noprocessfalse}
+\newcounter{psfig}
+\newif\if@pdfGINwidth
+\newif\if@pdfGINheight
+\newif\if@pdfGINscale
+\long\gdef\psgraphicsinclude{%
+ \@ifundefined{Fig\thepsfig}
+ {\PDFTWarningNoLine{pdftricks}
+ {******************************************\MessageBreak
+ ************ Processing Fig: \thepsfig\space**********\MessageBreak
+ ******************************************}
+ }
+ {\noprocess}
+ \ifPDFTshell\ifnoprocess\relax\else
+ \IfFileExists{\PDFTfigname.tex}{%
+ \immediate\write18{latex -interaction=batchmode \PDFTfigname}
+ \PDFTWarningNoLine{pdftricks}
+ {******************************************\MessageBreak
+ \PDFTfigname.tex converted to \PDFTfigname.dvi\MessageBreak
+ ******************************************}
+ }{}
+ \IfFileExists{\PDFTfigname.dvi}{%
+ \immediate\write18{dvips -o \PDFTfigname.ps \PDFTfigname}
+ \immediate\write18{ps2eps -f \PDFTfigname.ps}
+ \PDFTWarningNoLine{pdftricks}
+ {******************************************\MessageBreak
+ \PDFTfigname.eps generated\MessageBreak
+ ******************************************}
+ }{}
+ \IfFileExists{\PDFTfigname.eps}{%
+ \immediate\write18{epstopdf \PDFTfigname.eps}
+ \PDFTWarningNoLine{pdftricks}
+ {******************************************\MessageBreak
+ \PDFTfigname.eps converted to \PDFTfigname.pdf\MessageBreak
+ ******************************************}
+ }{}
+ \ifmiktex%
+ \immediate\write18{del \PDFTfigname.aux \PDFTfigname.dvi \PDFTfigname.log \PDFTfigname.eps} %%%%%% LDL-6
+ \else
+ \immediate\write18{rm \PDFTfigname.aux \PDFTfigname.dvi \PDFTfigname.log \PDFTfigname.eps} %%%%%% LDL-5
+ \fi\fi
+ \fi
+ \IfFileExists{\PDFTfigname.pdf}%
+ {\begin{center}
+ \bgroup\fboxsep\@PDFboxsep\fboxrule\@PDFboxrule%
+ \color{\@PDFgraphiccolor}%
+ \fcolorbox{\@PDFgraphiclinecolor}{\@PDFgraphicbackground}%
+ {\if@pdfGINwidth%
+ \includegraphics[width=\@PDFgraphicwidth]{\PDFTfigname}\else%
+ \if@pdfGINheight%
+ \includegraphics[height=\@PDFgraphicheight]{\PDFTfigname}\else%
+ \if@pdfGINscale%
+ \includegraphics[scale=\@PDFgraphicscale]{\PDFTfigname}\else%
+ \includegraphics{\PDFTfigname}\fi\fi\fi%
+ }\egroup\end{center}%
+ \global\@pdfGINwidthfalse\let\@PDFgraphicwidth\relax
+ \global\@pdfGINheightfalse\let\@PDFgraphicheight\relax
+ \global\@pdfGINscalefalse\let\@PDFgraphicscale\relax
+ }{\PDFTnopdftrue}
+ \gdef\@PDFgraphiclinecolor{white}
+ \gdef\@PDFgraphicbackground{white}
+ \gdef\@PDFboxsep{0pt}
+ \gdef\@PDFboxrule{0pt}
+}
+\definecolor{gray30}{gray}{.70}
+\definecolor{gray10}{gray}{.90}
+\RequirePackage{keyval}
+\def\configure[#1][#2]{\setkeys{#1}{#2}
+ \PDFTWarning{pdftricks}{Reconfigured #1 parameter(s)\MessageBreak #2\MessageBreak}
+ }
+\define@key{pdfgraphic}{width} {\gdef\@PDFgraphicwidth{#1}\global\@pdfGINwidthtrue}
+\define@key{pdfgraphic}{height} {\gdef\@PDFgraphicheight{#1}\global\@pdfGINheighttrue}
+\define@key{pdfgraphic}{scale} {\gdef\@PDFgraphicscale{#1}\global\@pdfGINscaletrue}
+\define@key{pdfgraphic}{color} {\gdef\@PDFgraphiccolor{#1}}
+\define@key{pdfgraphic}{linecolor} {\gdef\@PDFgraphiclinecolor{#1}}
+\define@key{pdfgraphic}{background}{\gdef\@PDFgraphicbackground{#1}}
+\define@key{pdfgraphic}{linewidth} {\gdef\@PDFboxrule{#1}}
+\define@key{pdfgraphic}{rulesep} {\gdef\@PDFboxsep{#1}}
+\gdef\@PDFgraphiccolor{black}
+\gdef\@PDFgraphiclinecolor{white}
+\gdef\@PDFgraphicbackground{white}
+\gdef\@PDFboxrule{0pt}
+\gdef\@PDFboxsep{0pt}
+%%
+%% Tweak to grab all the packages used in the master doc.
+%% This forces you to load pdftricks as the first package.
+%%
+\newenvironment{psinputs}{\begingroup
+ \newwrite\CVinputs
+ \immediate\openout\CVinputs=tmp.inputs
+ \def\usepackage{\@ifnextchar[\@CVUsepackage\@@CVUsepackage}
+ \def\@CVUsepackage[##1]##2{\immediate\write\CVinputs%
+ {\string\usepackage[##1]{##2}}}
+ \def\@@CVUsepackage##1{\immediate\write\CVinputs%
+ {\string\usepackage{##1}}}
+ }
+ {\endgroup\immediate\closeout\CVinputs}
+%%
+%% Arrays to keep the fig numbers
+%%
+\newcounter{arraylength}%
+\newcounter{ArrayIndex}%
+\newcounter{zeroCtr}%
+\newcounter{recordCtr}
+\setcounter{recordCtr}{1}
+\newcounter{Ctr}
+\def\DeclareArray#1{\Array{#1}[0]{}}%
+%
+\def\Array#1[#2]#3{%
+ \expandafter\gdef\csname #1#2\endcsname{#3}%
+ \expandafter\gdef\csname #1\endcsname[##1]{\csname #1##1\endcsname}}%
+%
+\def\getArraylength#1{\setcounter{arraylength}{0}%
+ \loop\expandafter\ifx\csname #1\thearraylength\endcsname\relax%
+ \else\stepcounter{arraylength}\repeat}%
+%
+\def\addToArray#1#2{\setcounter{arraylength}{0}%
+ \loop\expandafter\ifx\csname #1\thearraylength\endcsname\relax%
+ \else\stepcounter{arraylength}\repeat%
+ \Array{#1}[\thearraylength]{#2}}%
+%
+\def\clearArray#1{\getArraylength{#1}%
+ \loop\ifnum\c@arraylength >0%
+ \global\expandafter\let\csname #1\thearraylength\endcsname\relax%
+ \addtocounter{arraylength}{-1}\repeat}%
+%
+\long\def\ArrayIterator#1#2{%
+ \setcounter{ArrayIndex}{1}\getArraylength{#1}%
+ \setcounter{zeroCtr}{\c@arraylength}%
+ \loop\ifnum\c@ArrayIndex<\c@zeroCtr{#2}%
+ \stepcounter{ArrayIndex}\repeat%
+}%
+\def\@nnil{\@nil}
+\def\@empty{}
+\def\@cvrstop#1\@@#2{}
+%%
+%% Equivalent of \@tfor and \@for where any delimiter can be
+%% provided instead of LaTeX's default comma character
+%%
+\long\def\cvr@delimfor#1#2#3{\DeclareArray{#1}\clearArray{#1}%
+ \long\def\@icvrloop##1#2##2\@@##3{\def##3{##1}\ifx ##3\@nnil%
+ \expandafter\@cvrstop \else\addToArray{#1}{##1}%
+ \relax\expandafter\@icvrloop\fi##2\@@##3}%
+ \long\def\@cvrloop##1#2##2#2##3\@@##4{\addToArray{#1}{##1}%
+ \def##4{##1}\ifx ##4\@nnil \else%
+ \def##4{##2}\def\y@y{##2}\ifx\y@y\@nnil\else%
+ \addToArray{#1}{##2}\fi\ifx ##4\@nnil \else%
+ \@icvrloop ##3\@@##4\fi\fi}%
+ \expandafter\def\expandafter\@fortmp\expandafter{#3}%
+ \ifx\@fortmp\@empty \else%
+ \expandafter\@cvrloop#3#2\@nil#2\@nil\@@\@ee@\fi}%
+%
+% Dont look into the following code. It is harmful
+% for your eyes and brain as well.
+%
+\newcounter{f@irstCtr}
+\newcounter{s@econdCtr}
+\long\gdef\NoProcess[#1]{%
+ \long\def\@i@@noprocess##1,##2\@@##3{\def##3{##1}\ifx ##3\@nnil%
+ \expandafter\@cvrstop \else
+ \expandafter\hyphencheck##1-@-*[*]
+ \relax\expandafter\@i@@noprocess\fi##2\@@##3}%
+ \long\def\@@@noprocess##1,##2,##3\@@##4{
+ \expandafter\hyphencheck##1-@-*[*]
+ \def##4{##1}\ifx ##4\@nnil \else%
+ \def##4{##2}\def\y@y{##2}\ifx\y@y\@nnil\else%
+ \expandafter\hyphencheck##2-@-*[*]
+ \fi\ifx ##4\@nnil \else%
+ \@i@@noprocess ##3\@@##4\fi\fi}%
+ \expandafter\def\expandafter\@fortmp\expandafter{#1}%
+ \ifx\@fortmp\@empty \else%
+ \expandafter\@@@noprocess#1,\@nil,\@nil\@@\@ee@\fi}%
+\def\d@d#1[*]{}
+\def\hyphencheck#1-#2-#3{\def\r@r{@}\def\s@s{*}\edef\c@c{#3}
+ \ifx\c@c\r@r
+ \setcounter{f@irstCtr}{#1}
+ \setcounter{s@econdCtr}{#2}
+ \stepcounter{s@econdCtr}
+ \loop\ifnum\thes@econdCtr > \thef@irstCtr%
+ \expandafter\edef\csname Fig\thef@irstCtr\endcsname{TRUE}
+ \stepcounter{f@irstCtr}
+ \repeat%
+ \else\ifx\c@c\s@s%
+ \expandafter\edef\csname Fig#1\endcsname{TRUE}
+ \fi\fi\d@d}
+
+%%
+%%
+%% End of file `pdftricks.sty'
+%%
diff --git a/upstream/org.ibex.core/doc/reference/reference.xml b/upstream/org.ibex.core/doc/reference/reference.xml
new file mode 100644
index 0000000..28f8a34
--- /dev/null
+++ b/upstream/org.ibex.core/doc/reference/reference.xml
@@ -0,0 +1,2422 @@
+
+
+
+
+
+ **
+ If you are reading the html version of this document and are
+ thinking of printing it out, you might be interested in the nicely
+ typeset produced
+ with LaTeX.
+ **
+
+ This document is a __reference__. It is not a
+ __specification__ or a
+ __tutorial__.
+
+ This document does not guide the user gently through examples (as a
+ tutorial would), and it doesn't provide enough detail and formality
+ for a third party to construct a compatible re-implementation of the
+ Ibex Core (as a specification would).
+
+ Rather, the goal of this document is to completely describe every
+ aspect of the environment that the Ibex Core provides to client
+ applications, from the bottom up. If you want to be an Ibex expert,
+ this is the right document to read. It is assumed that you are already
+ familiar with XML and with either JavaScript or ECMAscript.
+
+ If you need to use or rely on some behavior you notice in the Ibex
+ Core, but which is not clearly defined here, please post to .
+
+
+
+
+ Ibex itself; the native code (or Java bytecode) that runs on
+ the client. This term does not include the **Wildebeest**
+ or the **UI**
+
+
+ A set of files (mostly XML, JavaScript, and PNG images)
+ bundled up in a zip archive, ending with the "[[.ibex]]"
+ extension. Together, these files specify the appearance and
+ behavior of the application's user interface.
+
+
+
+ We will use the term "the server" to refer to any other
+ computer which the client makes XML-RPC or SOAP calls
+ to. Note that it is possible for the client and server to be
+ the same machine.
+
+
+ This is a very small piece of code that is downloaded the
+ first time a client uses Ibex. It downloads the Ibex core,
+ verifies its signature, and launches it with the appropriate
+ parameters indicating where to find the initial UI.
+ Wildebeest works differently on every platform, and is outside
+ the scope of this document.
+
+
+ In ECMAscript, when you change the value of a property on an
+ object, you are **putting** to that property, or
+ **writing** to it. For example, the ECMAscript expression
+ "[[foo.bar = 5]]" **puts** the value 5 to the bar
+ property on object foo.
+
+
+ In ECMAscript, when you access the value of a property on an
+ object, you are **getting** that property, or
+ **reading** from it. For example, the ECMAscript
+ expression "[[return (3 + foo.bar)]]" **gets** the
+ value of bar property on object foo and then adds 3 to it
+ before returning the result.
+
+
+ We will use the terms JavaScript and ECMAScript
+ interchangeably in this document. The Ibex interpreter is not
+ completely ECMA-compliant, however (see for details).
+
+
+
+
+
+
+
+
+ A user typically begins an Ibex session by clicking on a link to
+ an Ibex application. This link serves the {\tt launch.html} file
+ to the user's browser, which in turn downloads the appropriate
+ {\it Wildebeest} -- currently either an ActiveX Control
+ (Win32/MSIE), XPInstaller (Mozilla), or Signed Applet (all
+ others).
+
+ The Wildebeest downloads the appropriate core for the user's
+ machine and verifies its digital signature. It then launches the
+ core, which downloads the UI (an [[.ibex]] archive), loads it,
+ applies the [[main.t]] template (found in the archive), and
+ renders it onto the screen, running any associated JavaScript
+ code.
+
+ The user interacts with the application by clicking and moving the
+ mouse, and by pressing keys on the keyboard. These actions trigger
+ fragments of JavaScript code which are designated to handle events.
+ This JavaScript code can then relay important information back to the
+ server using XML-RPC or SOAP, or it can modify the structure and
+ properties of the user interface to change its appearance, thereby
+ giving feedback to the user.
+
+ The Ibex core quits when the last remaining surface has been destroyed.
+
+
+
+
+
+
+
+ Each top-level window in an Ibex UI is called a
+ **surface**. There are two kinds of surfaces: **frames**,
+ which usually have a platform-specific titlebar and border, and
+ **windows**, which never have any additional platform-specific
+ decorations.
+
+ Whenever we refer to the size or position
+ of a surface, we are referring to the size or position of the
+ UI-accessible portion of the surface; this does not include any
+ platform-specific decorations. This means that if you set the
+ position of a frame to (0,0), the platform-specific titlebar will
+ actually be off the screen on most platforms (it will be above and
+ to the left of the top-left corner of the screen).
+
+ Surfaces are not actual JavaScript objects; you cannot obtain a
+ reference to a surface. However, each surface is uniquely identified
+ by its **root box**, described in the next section.
+
+
+
+
+
+ A **box** is the fundamental unit from which all Ibex user
+ interfaces are built. Boxes can contain other boxes (referred to as
+ its **children**). Each surface has a single box associated with
+ it called the **root box**; the root box and its children (and
+ its children's children, and so on) form the surface's **box
+ tree**.
+
+ There are three ways to think of a box:
+
+
+ As a rendered visualization on the screen (the "**Visual Representation**")
+
+ As a JavaScript object (the "**Object Representation**")
+
+ As as an XML tag (the "XML Template Representation").
+
+
+
+
+
+ All three representations are equally valid, and being able to
+ figure out what an action in one representation would mean in terms
+ of the other two representations is crucial to a solid understanding
+ of Ibex.
+
+
+
+
+
+
+ A template (discussed in the next section) is an XML file which acts
+ as a blueprint for constructing a tree of boxes. We call this
+ construction process **applying**, since unlike
+ **instantiation** in object-oriented programming systems, you
+ always apply a template to a pre-existing box, and you can apply
+ multiple templates to the same box.
+
+ Each XML tag corresponds to a single box, or to another template
+ which will be applied to that box. For example, a [[scrollbar]]
+ template, when applied, will construct a tree of boxes which has the
+ visual appearance and behavior of a scrollbar.
+
+ Although it is useful to think of the XML tags as being boxes, keep
+ in mind that the XML representation is only a blueprint for
+ constructing a tree of JavaScript objects. Once the template has
+ been instantiated, the XML is effectively "thrown away", and
+ JavaScript code is free to alter the boxes. Once the process of
+ applying a template is complete, Ibex completely forgets the fact
+ that it has applied a particular template to a particular box. One
+ consequence of this approach is that if you think of templates as
+ classes, then Ibex has no equivalent for Java's [[instanceof]]
+ operator.
+
+ Each template is an XML document whose root element is
+ [[<ibex>]]. Here is a sample template file:
+
+
+
+
+
+ This is a cool widget.
+
+
+ // this code is executed only once
+ static = { instances : [] };
+
+ // this element applies the ibex.lib.focusable template
+
+
+
+ static.instances.push(thisbox);
+
+
+
+
+
+
+
+
+
+ The following two namespaces are predefined and need not be declared
+ using [[xmlns]]:
+
+
+ [[http://xmlns.ibex.org/meta]]
+
+ This will be referred to as the "[[meta]] namespace" in the
+ remainder of this document.
+
+
+
+ [[http://xmlns.ibex.org/ui]]
+
+ This will be referred to as the "[[ui]] namespace" in the
+ remainder of this document.
+
+
+ Additionally, the default namespace for the document will be set to
+ the template's package FIXME.
+
+
+
+ If the root [[<ibex>]] element contains any non-whitespace
+ text content, this text is interpreted as JavaScript code and is
+ executed the first time the template is referenced. This code is
+ executed in a fresh scope containing two predefined properties:
+
+
+ The Ibex Object (described in )
+
+
+
+ A reference to this template's **static object**, which is
+ initially [[null]]. The static object can be accessed (read
+ and written) from both static scripts as well as instance
+ scripts in a particular template. FIXME
+
+
+
+
+
+ Any immediate children of the root element which are in the
+ [[meta]] namespace are treated as metadata and are exempted from
+ the rest of the template application process. Currently only one
+ type of metadata element is defined:
+
+
+ [[<meta:doc>]]: structured documentation for the
+ template.
+
+
+
+
+
+
+ All remaining children of the root element are treated as elements
+ to be **applied** to the box, in the order in which they appear
+ in the file using the following procedure.
+
+
+
+
+
+ ... FIXME
+
+ During a box initialization, script-private references to a
+ box's descendants with [[id]] attributes are placed on the
+ box. These references allow scripts on that box to easily refer
+ to descendant nodes created by the template in which the script
+ appears. For example, these two blocks of code have exactly the
+ same effect:
+
+
+
+
+
+ To apply an XML tag [[__x__]] to a box [[__b__]], perform the following
+ operations, in this order:
+
+
+
+ Allocate a fresh scope [[__s__]] whose parent scope is
+ [[__b__]].
+
+ Process each child element or text segment of [[__x__]]
+ in the order they appear in the document:
+
+
+
+ Treat each text segment [[__t__]] as JavaScript code
+ and execute it with [[__s__]] as the root scope.
+
+ For each child element [[__x'__]] of [[__x__]]:
+
+
+ Create a new box [[__b'__]].
+
+ If the name of tag [[__x'__]] is not
+ "[[box]]" in the [[ui]] namespace, prepend the
+ tag's namespace identifier uri (if any) to the name of
+ the tag, and use the result as a key to retrieve a
+ property from the root stream (defined later).
+ Interpret the resulting stream as a template and apply
+ that template to [[__b'__]].
+
+ (recursively) apply [[__x'__]] to [[__b'__]].
+
+ If [[__x'__]] has an [[id]] attribute, declare a variable
+ in [[__s__]] whose name is the value of the [[id]]
+ attribute, prefixed with the [[$]] character, and whose
+ value is [[__b'__]]
+
+ Copy any [[$]]-variables created during the application
+ of [[__x'__]] into scope [[__s__]].
+
+ Append [[__b'__]] as the last child of [[__b__]].
+
+
+
+ Apply any attributes on [[__x__]] to [[__b__]], except for
+ [[id]]. Since XML specifies that the order of attributes
+ cannot be significant, Ibex processes attributes in
+ alphabetical order by attribute name. For example, if
+ [[__x__]] has the attribute [[foo="bar"]], then the
+ equivalent of the statement [[B.foo="bar";]] will be
+ performed, with the following exceptions:
+
+
+ If the value portion of the attribute is the string
+ "[[true]]", put the boolean [[true]]. If the
+ value is "[[false]]", put the boolean [[false]].
+
+ If the value is a valid ECMAscript number, put it as a
+ number (instead of a string).
+
+ If the value begins with a dollar sign ([[$]]),
+ retrieve the value of the corresponding variable in
+ [[__s__]] and use that value instead.
+
+ If the value begins with a dot ([[.]]), prepend the
+ attributes' namespace identifier uri (if any) and
+ interpret the remainder as a property to be retrieved from
+ the root stream (defined later).
+
+
+
+
+
+ The last two steps are referred to as the **initialization** of the
+ node. There are two important aspects of this ordering to be aware of:
+
+
+
+ A given box will be fully initialized before its parent is
+ given a reference to that box. This way, parents can be
+ certain that they will never wind up accessing a box when it
+ is in a partially-initialized state.
+
+ Attributes are applied **after** scripts are run so that
+ the attributes will trigger any **traps** (defined later)
+ placed by the script.
+
+
+
+
+
+
+
+
+
+
+ Each box occupies a rectangular region on the surface. The visual
+ appearance of a surface is created by rendering each box in its tree.
+ Unless the [[clip]] attribute is [[false]], each box will
+ clip its childrens' visual representations to its own, so that the
+ children appear "confined to" the parent. Children are rendered after
+ their parents so they appear "on top of" their parents.
+
+ Each box has two major visual components, each with subcomponents:
+
+ FIXME: diagram
+
+
+
+ A box's [[path]] consists of zero or more lines and curves.
+ The path may be filled with a color, gradient, or texture, and
+ may be stroked with a line of a given thickness and color. If
+ the path is not specified, it defaults to the perimiter of the
+ box. [**Note: Vector Graphics support (including the ability
+ to set the [[path]] property to anything other than the
+ default) is currently not implemented**].
+
+ A path also has:
+
+
+ an associated [[strokecolor]], which is a color
+
+ an associated [[strokewidth]], which is a number
+ specifying the width of the stroke. [**Note: Vector
+ Graphics support (including the [[strokewidth]]
+ property) is currently not implemented**]
+
+ a [[fill]], which is either a color, gradient, or
+ texture
+
+
+
+
+
+ Each box also has a single line of [[text]], whose
+ appearance is determined by its:
+
+
+ associated [[font]], which can be any font supported by
+ the
+ library.
+
+ an associated [[fontsize]] in **pixels**
+
+ an associated [[textcolor]]
+
+
+
+ These eight components plus the size of a box fully specify its
+ appearance. Every single box you see in Ibex is drawn only on the
+ basis of these components and its size.
+
+ The size and position of every box is determined by its
+ properties, its childrens' sizes, and its parent's size and
+ position. Box layout and rendering happens in four phases:
+ **packing**, **constraining**, **placing**, and
+ **rendering**. The Core is careful to only perform a phase on
+ a box if the box has changed in a way that invalidates the work
+ done the last time that phase was performed. The packing and
+ constraining phases are performed in a single traversal of the
+ tree (packing is preorder, constraining is postorder), and the
+ placing and rendering phases are performed in a second traversal
+ of the tree (first placing, then rendering, both preorder).
+
+ For brevity, the rest of this chapter deals only with width and
+ columns. Height and rows is treated identically and independently.
+ Also, it is important to note that the term **minimum width** is
+ not the same thing as the property [[minwidth]], although they
+ are closely related.
+
+
+
+ When the user resizes a window, Ibex changes the root box's
+ [[maxwidth]] and [[maxheight]] to match the size chosen by
+ the user and then determines the root box's size using the same sizing
+ rules it uses for other boxes.
+
+ Ibex will always attempt to prevent the
+ user from making the surface smaller than the root box's
+ [[minwidth]] and [[minheight]]. If the [[hshrink]] or
+ [[vshrink]] flag is set, Ibex will try to prevent the user from
+ resizing the surface at all. However, not all platforms give Ibex
+ enough control to do this.
+
+
+
+ When talking about positioning, we will often refer to the
+ **alignment point**.
+
+ If the [[align]] property is "[[center]]", then the
+ alignment point is the center of the box.
+
+ If the [[align]] property is "[[topleft]]",
+ "[[bottomleft]]", "[[topright]]", or
+ "[[bottomright]]", then the alignment point is
+ corresponding corner of the box.
+
+ If the [[align]] property is "[[top]]",
+ "[[bottom]]", "[[right]]", or "[[left]]", then
+ the alignment point is middle of the corresponding edge of the
+ box.
+
+
+
+ A grid of **cells** is created within the parent. If the
+ parent's [[cols]] property is set to 0, the cell grid has an
+ infinite number of columns. Either [[cols]] or [[rows]]
+ must be zero, but not both.
+
+ If a child's [[visible]] property is [[false]], it does
+ not occupy any cells (and is not rendered). Otherwise, each child
+ occupies a rectangular set of cells [[child.colspan]] cells
+ wide and [[child.rowspan]] cells high.
+
+ The Core iterates over the cells in the grid in the following
+ order: if [[rows]] is 0, the Core iterates across each column
+ before proceeding to the next row; otherwise rows come before
+ columns. At each cell, the Core attempts to place the **first
+ remaining unplaced child's** top-left corner in that cell
+ (with the child occupying some set of cells extending down and
+ to the right of that cell). If the parent has a fixed number of
+ columns and the child's [[colspan]] exceeds that limit, the
+ child is placed in column zero regardless, but only occupies the
+ available set of cells (it does not "hang off the end" of the
+ box).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Each box's minimum width is computed recursively as the
+ maximum of:
+
+
+ Its [[minwidth]]
+
+ The width of the box's [[text]] (after applying the
+ box's [[transform]]) [**Note: Vector Graphics support
+ (including the [[transform]] property) is currently not
+ implemented**].
+
+ The width of the box's path (after applying the box's
+ [[transform]]) **if the box is [[packed]]**.
+
+ The minimum width of the children in each row.
+
+
+ If a box's [[hshrink]] property is set to
+ [[true]], the box's maximum width is the same as its
+ minimum width; otherwise it is the box's
+ [[maxwidth]].
+
+
+
+
+
+
+
+ Each non-packed box is transformed according to the parent's
+ [[transform]] property and then positioned so that its alignment
+ point is [[(child.x, child.y)]] pixels from the corresponding
+ edge/corner/center of its parent.
+
+
+
+ Ibex formulates a set of constraints for placing a box's
+ **packed** children as follows:
+
+
+ - A box's width can be no greater than the sum of the
+ columns it occupies
+ - The sum of a set of colums cannot be smaller than the
+ minimum width of a box that spans them.
+ - The sum of the widths of the parents' columns will be at
+ least as large as the parent's width is (but possibly
+ larger).
+
+
+ Subject to these two unbreakable constraints, Ibex searches for
+ a solution which will optimize the following three goals,
+ prioritized from most important to least important:
+
+
+ - (__Most Important__) The sum of all columns will be a close
+ to the parent's with as possible (ie as small as possible)
+ - Ibex will attempt to make a set of columns no wider than
+ the [[maxwidth]] of a box spanning them.
+ - (__Least Important__) Ibex will attempt to make all
+ columns the same width.
+
+
+ Each packed box is then placed within the set of cells that it
+ spans. Usually the box will exactly fill this rectangle; if it
+ does not (due to [[maxwidth]] or minimum width constraints), the
+ box's will be placed so that its alignment point coincides with
+ the alignment point of that rectangle of cells.
+
+
+
+
+
+ Boxes are rendered in a depth-first, pre-order traversal. Note that
+ this may cause a non-packed box to overlap its siblings.
+
+
+
+ If the box's [[transform]] property is non-null, the
+ coordinate space is transformed accordingly for the rest of
+ this phase and for the rendering of all children. [**Note:
+ Vector Graphics support (including the [[transform]]
+ property) is currently not implemented**].
+
+ If the box is packed and has a non-[[null]] path, the
+ path is translated such that the alignment point of the path's
+ bounding box coincides with the box's alignment point (both
+ alignment points are determined by the box's [[align]]
+ property).
+
+ If a box has a path, that path is filled with the color,
+ gradient, or image specified by the [[fill]] property and
+ stroked with the color and width specified by the
+ [[strokecolor]] and [[strokewidth]] properties.
+
+ If the box has a non-null [[text]] attribute,
+ the text is rendered in [[font]] with size
+ [[fontsize]] and color [[textcolor]]. The text is
+ then translated such that the alignment point of the text's
+ bounding box coincides with the box's alignment point (both
+ alignment points are determined by the box's [[align]]
+ property).
+
+ The box's children are rendered (pre-order traversal).
+
+
+
+
+
+
+
+
+
+ Each box is a full-fledged ECMAscript object, and can store
+ key-value pairs as properties. Some of these keys have special
+ meaning, which will be explained later. Each box's numeric
+ properties hold its **child boxes**.
+
+
+
+ Every box has several special properties which control how it is
+ drawn. In general, if you put an
+ invalid value to a special property, no action will be taken -- the
+ put will be ignored.
+
+
+
+ If the value is a 5-character hex string ([[#RGB]]),
+ 7-character hex string ([[#RRGGBB]]), 9-character hex
+ string ([[#AARRGGBB]]), the box's stroke color will be set
+ to that color.
+
+ If the value is one of the colors (the same set of color names
+ supported by SVG), the stroke color be set to that color.
+
+ If the value is [[null]], the stroke color will be set
+ to clear ([[#00000000]]).
+
+
+
+ The width (in pixels) to stroke the path with.
+
+
+
+ This property can be set to any of the values specified for
+ [[strokecolor]].
+ Alternatively, if the value written is an object, its stream
+ will be read and interpreted as a PNG, GIF, or JPEG image,
+ which will become the texture for this box, and the box's
+ [[minwidth]] and [[minheight]] properties will be
+ automatically set to the dimensions of the image.
+
+
+
+ The box's path. The grammar and feature set supported are
+ identical to that specified in .
+
+
+
+ The box's text; writing [[null]] to this property sets it
+ to [[""]].
+
+
+
+ The color in which to render the font; accepts the same values as [[strokecolor]].
+
+
+
+ When an object is written to this property, its stream is read
+ using the ,
+ and the resulting font is used to render the
+ box's [[text]].
+
+
+
+ The size (in points) to render the text.
+
+
+
+
+
+
+
+ If set to [[true]], this box will shrink
+ (horizontally/vertically/both) to the smallest size allowed by
+ its children and the bounding box of its path.
+
+
+
+ If the box is a root box, this is the (x/y)-coordinate of the
+ surface; otherwise it is the distance between the parent's
+ alignment point and the corresponding corner/edge/center of
+ its parent.
+
+
+
+ The desired minimum width and height.
+
+
+
+ The desired maximum width and height.
+
+
+
+ When read, this is the current (width/height) of this box.
+ Writing to this property is equivalent to writing to
+ **both** the minimum and maximum (width/height).
+
+
+
+ The number of (columns/rows) in which to lay out the children of this
+ box. If set to zero, the number of (columns/rows) is unconstrained.
+ Either [[rows]] or [[cols]] must be zero. If
+ [[0]] is written to [[cols]] when [[rows]] is
+ [[0]], the write is ignored. If a nonzero value is
+ written to [[cols]] when [[rows]] is nonzero,
+ [[rows]] is set to [[0]], and vice versa.
+
+
+
+ The number of (columns/rows) that this box spans within its parent.
+
+
+
+ Determines the box's alignment point for positioning its text,
+ texture, path, and children.
+
+
+
+ If set to [[false]], this box will be rendered as if its
+ width and height were zero. If this is a root box, the
+ associated surface will be hidden.
+
+ When reading from this property, the value [[false]] will
+ be returned if this box **or any of its ancestors** is not
+ visible. Thus it is possible to write [[true]] to a box's
+ [[visible]] property and then read back [[false]].
+
+
+
+ The layout strategy for this box. If set to [[true]], the
+ box occupies no cells and is laid out independently of its
+ siblings.
+
+
+
+
+
+
+
+ Writing to this property sets the box's redirect target. This
+ property cannot be read from, and can only be written to if
+ the value being written is a **descendant** of the current
+ value.
+
+ If a box has a non-null redirect target, reads and writes to
+ any of the other properties in this section will be forwarded
+ to the redirect target.
+
+ The [[redirect]] attribute is very useful for hiding the
+ internal structure of a widget, and for allowing widgets to act as
+ "smart" containers for other widgets. For example, a menu widget might
+ have an invisible child as its redirect target; this way, when boxes
+ representing items on the menu are added as children of the menu
+ widget, they do not appear until the menu is pulled down.
+
+
+
+ The **n**th child of box [[b]] can be accessed by reading from
+ [[b[n]]]. The **n**th child can be removed by writing
+ [[null]] to [[b[n]]] (the child will become parentless). A
+ new child can be inserted **before** the **n**th child by
+ writing it to [[b[n]]]; if the value written is already a child of
+ [[b]], it will be removed from [[b]] first. It is important
+ to note that this behavior is different from ECMAscript arrays --
+ writing a non-[[null]] value to [[b[n]]] does not eliminate
+ the **n**th child; it merely shifts it over one position.
+ __Note:__ Unlike most JavaScript objects, enumerating a Box's
+ properties with the JavaScript [[for..in]] construct will
+ enumerate **only** the box's children and not any other properties.
+
+
+
+ If [[true]], the visual representation of this box's
+ children will be clipped to the boundaries of this box.
+ __Note:__ setting this property to [[false]] imposes a
+ substantial performance penalty.
+
+
+
+ The number of children this box has.
+
+
+
+
+ FIXME
+ If this box has a parent, this property returns
+ [[**parent**.surface]]; otherwise it returns null.
+ This property is a simple building block that the widget
+ library uses to implement more complex functionality such as
+ focus control and popups.
+
+
+
+
+
+
+
+ The shape that the cursor should take when inside this
+ box. Valid values are: "[[default]]" , "[[wait]]",
+ "[[crosshair]]", "[[text]]", "[[hand]]", and
+ "[[move]]", as well as resizing cursors"[[east]]",
+ "[[west]]", "[[north]]", "[[south]]",
+ "[[northwest]]", "[[northeast]]",
+ "[[southwest]]", and "[[southeast]]". Note that on
+ some platforms, resize cursors for opposite directions (such
+ as [[northwest]] and [[southeast]] are the
+ same).
+ If a box's cursor is [[null]], its parent's cursor will
+ be used. If the root box's cursor is null, the
+ "[[default]]" cursor will be used.
+
+
+
+ Reading from this property will return the parent scope used
+ to execute the [[]] block of the template
+ in which the currently-executing code resides.
+
+
+
+ Returns a reference to the box itself.
+ If [[null]] is written to this property, and this box is
+ the root box of a surface, the box will be detached and the
+ surface destroyed. If this box has a parent, it will be
+ detached from its parent.
+
+
+
+ This property is actually a function; invoking
+ [[parent.indexof(child)]] will return the numerical index
+ of [[child]] in [[parent]] if [[child]] is a
+ child of [[parent]] (or [[parent]]'s redirect
+ target), and [[-1]] otherwise. Writing to this property
+ has no effect.
+
+
+
+ This property is actually a function; invoking
+ [[box.distanceto(otherbox)]] will return an object with two
+ properties, [[x]] and [[y]], containing the horizontal
+ and vertical distance between the two boxes (negative if
+ [[otherbox]] is to the left of / above [[box]]). This
+ can be used to determine the relative placement of two boxes
+ on different branches of the box tree.
+
+
+
+
+
+
+ The following special properties are only meaningful on the root box
+ of a surface.
+
+
+ The value [[true]] is put to this property on the root box
+ when the surface gains the input focus, and [[false]] when
+ the surface loses the input focus. Reading from this value will
+ return [[true]] if the surface is focused and [[false]]
+ if it is not. Putting [[true]] to this property will
+ **not** cause the surface to "steal" the input focus from other
+ windows.
+
+
+
+ The value [[true]] is put to this property on the root box
+ when the surface is maximized, and [[false]] when the surface
+ is un-maximized. Reading from this value will return [[true]]
+ if the surface is maximized and [[false]] if it is
+ not. Putting [[true]] to this property will maximize the
+ window, and putting [[false]] to this property will
+ unmaximize the window.
+ Note that not all platforms support maximization.
+
+
+
+ The value [[true]] is put to this property on the root box
+ when the surface is minimized, and [[false]] when the surface
+ is unminimized. Reading from this value will return [[true]]
+ if the surface is minimized and [[false]] if it is
+ not. Putting [[true]] to this property will minimize the
+ window, and putting [[false]] will unminimize it.
+
+
+
+ When the user attempts to close a surface, the value
+ [[true]] will be put to this property. Scripts may trap
+ this property to prevent the window from closing. Putting the
+ value
+ [[true]] to this property on a root box has the same
+ effect as putting [[null]] to the [[thisbox]]
+ property.
+
+
+
+ The surface's icon. This is usually displayed on the titlebar of a
+ window. The value should be an object whose stream is a PNG image. Note
+ that not all platforms support this property.
+
+
+
+ The surface's titlebar text. Note that not all platforms support
+ this property. Only ASCII characters 0x20-0x7F are permitted.
+
+
+
+
+
+
+
+
+
+
+
+ Every object has a **stream** associated with it. A stream is a
+ sequence of bytes that can be read or written to.
+
+ By default an object has an empty stream (zero bytes). However, some objects
+ (returned from special methods on the [[ibex]] object) have
+ streams yielding data read from an url, file, or a component of a zip
+ archive. In a future release, the stream associated with a box will
+ be an .ibex template which, when applied, will fully reconstitute the
+ box's state.
+
+
+
+ Despite the ubiquity of streams, you cannot actually reference a
+ stream, since it is not an object. Instead, you simply reference the
+ object it belongs to. If you are familiar with Java, this is similar
+ to how every Java object has a monitor associated with it, but you
+ cannot directly manipulate the monitor (you can't pass around a
+ reference to just the monitor).
+
+ In the rest of the section we will sometimes refer to "getting
+ properties from a stream" or "passing a stream to a function"; this is
+ just shorthand for saying to perform those actions on the object the
+ stream belongs to.
+
+
+
+ You can create a stream from a URL by calling
+
+
+ var r = ibex.stream.url("http://...");
+
+
+ This will return an object whose stream draws data from the specified
+ URL. Streams are loaded lazily whenever possible.
+
+
+
+
+
+ Most stream objects let you access
+ substreams using the usual JavaScript operators [[[]]] and
+ [[.]], as well as the [[for..in]] syntax.
+
+
+ // r1 and r2 are equivalent but not equal (!=)
+ var r1 = ibex.stream.url("http://www.ibex.org/foo/bar.html");
+ var r2 = ibex.stream.url("http://www.ibex.org/foo")["bar.html"];
+
+
+
+
+
+
+ The empty-string property on the [[ibex]] object is called the
+ **root stream**. You can access this object as [[ibex..]] or
+ [[ibex[""]]]. Additionally, any expression which starts with a
+ dot is treated as property to be retrieved from the root stream. The
+ following three expressions are equivalent:
+
+
+ ibex..foo
+ ibex[""].foo
+ .foo
+
+
+
+
+
+
+ FIXME
+
+ You can access variables within the static block of a template by
+ appending a double period ([[..]]) and the variable name to the
+ stream used to load that template:
+
+
+
+
+
+
+
+ If you attempt to send a stream as part of an XML-RPC call, the
+ stream will be read in its entirity, Base64-encoded, and transmitted
+ as a [[]] element.
+
+ Ibex supports two special URL protocols. The first is [[data:]],
+ which inteprets the rest of the URL as a Base64 encoded sequence of
+ bytes to use as a source. The other is [[utf8:]] which
+ interpretets the rest of the string as a Unicode character sequence to
+ be UTF-8 encoded as a string of bytes to use as a source.
+
+
+ var r5 = ibex.stream.url("data:WFWE876WEh99sd76f");
+ var r6 = ibex.stream.url("utf8:this is a test");
+
+
+ You can read a UTF-8 encoded string from a stream like this:
+
+
+ var myString = ibex.stream.fromUTF(ibex.stream.url("utf8:testing..."));
+
+ You can also parse XML from a stream using SAX like this:
+
+
+
+
+
+
+
+
+
+
+ The [[ibex]] object is present in the top-level scope of every
+ script. It has the following properties:
+
+
+
+
+ reading from this property returns a new box
+
+
+ creates a clone of object
+
+
+ returns a blessed clone of stream
+
+
+
+
+
+ reading from this property returns a new date
+
+
+ this object contains the ECMA math functions
+
+
+ return a regexp object corresponding to string **s**
+
+
+ this object contains the ECMA string manipulation functions
+
+
+
+
+
+ log the debug messages **m1** through **mn**.
+ **o**
+
+
+
+ log the info messages **m1** through **mn**.
+
+
+
+ log the warning messages **m1** through **mn**.
+
+
+
+ log the error messages **m1** through **mn**.
+
+
+
+
+
+ opens a new browser window with URL **u**
+
+
+
+ true if the control key is depressed
+
+
+
+ true if the shift key is depressed
+
+
+
+ true if the alt key is depressed
+
+
+
+ the name of the "alt" key (usually either "alt", "meta", or
+ "option")
+
+
+
+ the contents of the clipboard; can be read and written to
+
+
+
+ the maximum dimension of any UI element; usually
+ 231, but may be smaller
+
+
+
+ the width of the screen, in pixels
+
+
+
+ the height of the screen, in pixels
+
+
+
+ either 0, 1, 2, or 3, indicating the mouse button currently
+ being pressed
+
+
+
+ when a box is written to this property, it becomes the root
+ box of a new window
+
+
+
+ when a box is written to this property, it becomes the root
+ box of a new frame
+
+
+
+ an object whose stream is a a builtin serif font
+
+
+
+ an object whose stream is a builtin sans-serif font
+
+
+
+ an object whose stream is a a builtin fixed-width font
+
+
+
+
+
+ return an XML-RPC call object with endpoint URL **u**
+
+
+
+ return a SOAP call object with endpoint URL **u**,
+ SoapAction **a**, and XML Namespace **n**
+
+
+
+
+
+ when a function is written to this property, a new thread is
+ forked to call it
+
+
+
+ yield the current thread
+
+
+
+ sleep for **n** milliseconds
+
+
+
+
+
+ returns a new object whose stream is drawn from URL **u**
+
+
+
+ unpacks a zip archive from **s**'s stream
+
+
+
+ unpacks a cab archive from **s**'s stream
+
+
+
+ valign=top>wraps a disk-backed read cache keyed on **k**
+ around **s**'s stream
+
+
+
+ returns an object whose stream is drawn from **s**'s
+ stream, but invokes **f(n,d)** as it is read from.
+
+
+
+ Use SAX to parse the XML document on stream **s** with
+ handler **h**
+
+
+
+ Same as [[parse.xml()]], but tries to fix broken HTML.
+
+
+
+ treat **s**'s stream as a string encoded as a UTF-8 byte stream and return the string
+
+
+
+ [[ibex.stream.tempdir]]
+
+
+
+
+
+ **not implemented yet:** return a
+ stream which rsa-decrypts stream **s** with key **k**
+
+
+
+ **not implemented yet:** return a
+ stream which rc4-decrypts stream **s** with key **k**
+
+
+
+ **not implemented yet:** immediately
+ MD5-hash stream **s**
+
+
+
+ **not implemented yet:** immediately
+ SHA1-hash stream **s**
+
+
+
+
+
+
+ You can add a trap to a property by applying the [[++=]] operator
+ to a function with one argument. The trap will be invoked whenever
+ that property is written to.
+
+
+
+ If another script were to set the property "[[foo]]"
+ on the box above to the value [[5]], the function above would be
+ invoked with the argument [[5]]. The function would then log
+ the string "[[foo is 5]]".
+
+ Within a trap, the expression [[trapee]] can be used to
+ get a reference to the box on which the trap was placed.
+
+ The expression [[trapname]] returns the name of the
+ trap executing the current function. This is useful when a function
+ is applied to multiple traps. For example:
+
+
+
+
+
+
+
+ When the trapped property is **written** to, each of the trap
+ functions placed on it will be invoked in the opposite order that
+ they were placed on the box -- the most recently placed trap will
+ execute first. This last-to-first execution of traps is called
+ **cascading**. After the last trap is invoked, the value is
+ stored on the box (remember, boxes are objects, so they can hold
+ properties just like all other ECMAscript objects).
+
+
+
+ There are two additional tricks you can use when placing traps. The
+ first is a **manual cascade**. If you want to cascade to lower
+ traps in the middle of a function, or you want to cascade with a
+ different value than the value passed to you (in effect "lying" to
+ lower traps), you can use [[cascade]]. For example:
+
+
+
+ color ++= function(c) {
+ ibex.log.info("refusing to change colors!");
+ cascade = "black";
+ }
+
+
+
+ This effectively creates a box whose color cannot be changed, and
+ which complains loudly if you try to do so.
+
+ Do **not** try to do something like this:
+
+
+
+ color ++= function(z) {
+ color = "black"; // INFINITE LOOP! BAD!!!
+ }
+
+
+ To prevent automatic cascading, return [[true]] from your function:
+
+
+
+ color ++= function(z) {
+ return true; // the box's color will not change
+ }
+
+
+
+
+
+
+
+ The other trick is a **read-trap**. Read traps are just like normal
+ traps, except that you use a function that takes zero arguments instead of one. Read traps
+ also do not automatically cascade.
+
+
+
+ If another script attempts to read from the [[doublewidth]]
+ property on this box, the value it gets will be twice the actual width
+ of the box. Note that
+ the actual [[doublewidth]] property on the box never gets written
+ to, since the trap does not cascade.
+
+ You can manually cascade on read traps as well:
+
+
+
+ text [[++=]] function() { return "my text is " + cascade; }
+
+
+
+ Read traps are only rarely needed -- most of the time a write trap
+ should be enough.
+
+
+
+
+
+ To prevent confusing and hard-to-debug behaviors, scripts may not
+ place traps on any of the properties described in the sections
+ , , or except for [[childadded]],
+ [[childremoved]] and [[surface]]. FIXME: remove?
+
+
+
+ If an uncaught exception is thrown from a trap, Ibex will log the
+ exception, but will **not** propagate it to the code which
+ triggered the trap. If the trap was a read trap, the value
+ [[null]] will be returned.
+ FIXME: is this right?
+
+
+
+ Traps are the backbone of Ibex. Since almost all UI programming is
+ event/demand driven, traps eliminate the need for separate
+ member/getter/setter declarations, often cutting the amount of typing
+ you have to do to a third of what it would normally be.
+
+
+
+ **Cloning** is a companion technique for traps; together they can
+ be used to simulate any sort of environment you might need. When you
+ call [[ibex.clone(o)]], Ibex returns a new object (called the
+ **clone**) which compares with equality ([[==]]) to the
+ original object. Furthermore, both objects are "equal" as keys in
+ hashtables, so:
+
+
+ var hash = {};
+ var theclone = ibex.clone(o);
+ hash[o] = 5;
+ ibex.log.info(hash[theclone]); // prints "5"
+
+
+ Any writes to properties on the clone will actually write to
+ properties on the original object, and reads from properties on the
+ clone will read properties on the original object. In fact, the only
+ thing that can be used to distinguish the original from the clone is
+ traps -- a trap placed on the clone is **not** placed on the
+ original object as well.
+
+
+
+
+
+
+
+
+
+ From the perspective of an application writer, Ibex is strictly
+ single-threaded. Ibex is always in exactly one of the following three
+ **contexts**:
+
+
+
+ __Rendering Context__ -- (redrawing the screen)
+
+ __Event Context__ (executing javascript traps triggered by an event)
+
+ __Thread Context__ (executing a background thread spawned with [[ibex.thread]])
+
+
+
+ There are two important restrictions on what can be done in particular contexts:
+
+
+
+ The [[box.mouse]] property and its subproperties
+ ([[x]], [[y]], and [[inside]]) can only be read
+ from within the Event Context, or in a thread context
+ **after** a the [[box.mouse]] property on this box or
+ an ancestor box has been written to.
+
+ Blocking operations (anything that accesses the network or
+ disk) can only be performed in the Thread Context.
+
+
+
+
+
+
+
+ Ibex offers easy access to threads. Spawning a background thread is as
+ simple as writing a function to the [[ibex.thread]] property:
+
+
+ ibex.thread = function() {
+ ibex.log.info("this is happening in a background thread!");
+ }
+
+
+ The argument set passed to the function is currently undefined and is
+ reserved for use in future versions of Ibex. Scripts should not
+ depend on the number or content of these arguments.
+
+ Ibex is **cooperatively multitasked**, so threads must not process
+ for too long. This was a deliberate choice; cooperatively
+ multitasked environments do not require complex locking primitives
+ like mutexes and semaphores which are difficult for novices to
+ understand. The disadvantage of cooperative multitasking is that one
+ thread can hog the CPU. This is unlikely to happen in Ibex for two reasons:
+ first, all blocking I/O operations **automatically** yield the CPU,
+ so the overall user interface never becomes unresponsive because it is
+ waiting for a disk or network transfer. Second, since Ibex is strictly
+ a user interface platform, Ibex scripts are unlikely to perform highly
+ compute-intensive operations that keep the CPU busy for more than a
+ few milliseconds.
+
+
+
+
+
+
+ Every execution of the Event Context begins with an event, which
+ consists of a key/value pair, and a mouse position, which consists of
+ an x and y coordinate. The possible keys are [[_Press[1-3]]],
+ [[_Release[1-3]]], [[_Click[1-3]]], [[_DoubleClick[1-3]]],
+ [[_Move]], [[_KeyPressed]], and [[_KeyReleased]].
+
+ Here are two example events:
+
+ An event is triggered by writing the key to the value on a box. This
+ triggers any trap handlers which may be present. Once these handlers
+ have executed, Ibex figures out which child of the current box contains
+ the mouse (taking into account that some boxes may cover up others)
+ and writes the key and value to that box. If none of the box's
+ children contain the mouse position, Ibex removes the leading
+ underscore from the key name and writes the value to
+ **that** property. Once all the traps on that property have
+ executed, the value is written to the box's parent.
+
+ Intuitively, Ibex delivers the underscored event to every box from the
+ root to the target, and then delivers the non-underscored event to
+ that same set of boxes in reverse order. So the event travels down
+ the tree to the target, and then back up to the root. The following
+ example prints out "first second third fourth" in that order.
+
+
+
+ In general, you should use the **non-underscore** names to respond
+ to user input and use the underscored names when you want to override
+ child boxes' behavior or route events to particular boxes (for
+ example, when implementing a focus protocol). This is why the
+ underscored elements are delivered to parents before children (so
+ parents can override their childrens' behavior), but non-underscored
+ events are delivered to children before parents (since, visually, a
+ mouse click is usually "intended" for the leaf box underneath the
+ cursor).
+
+
+
+
+
+ At any point in this sequence, a trap handler can choose not to
+ cascade (by returning [[true]] from the trap handler function).
+ This will immediately cease the propagation of the event. This is how
+ you would indicate that an event has been "handled".
+
+
+
+ Ibex uses the following events to notify a box about changes that
+ only matter to that particular box. These events do not propagate
+ either up or down the tree.
+
+
+ The value [[true]] is written to this property when the mouse (enters/leaves) the box.
+
+
+
+ The value [[true]] is put to this property after the size
+ of this box changes.
+
+
+
+ When a child is added or removed, that child is written to
+ this property. The write is always performed **after** the
+ addition or removal, so these two cases can be distinguished
+ by checking [[indexof(child)]].
+
+ Note that if the parent's redirect target is set to another
+ box, this trap will only be invoked when children are
+ manipulated by reading and writing to the parent. Reads and
+ writes directly to the redirect target will **not** trigger
+ this trap.
+
+ Note also that this traps is still triggered if a box's
+ [[redirect]] target is **null**. This is useful for
+ boxes that need to accept children and then relocate them
+ elsewhere.
+
+
+
+
+
+ Indicates that the use has pressed a mouse button. On
+ platforms with three mouse buttons, the **middle** button
+ is button 3 -- this ensures that applications written to only
+ use two buttons (1 and 2) will work intuitively on three button
+ platforms.
+
+
+
+ Indicates that the use has released a mouse button.
+
+
+
+ Indicates that the user has pressed and released the
+ mouse button without moving the mouse much (exactly how
+ much is platform-dependent).
+
+
+
+ Indicates that the user has clicked the
+ mouse button twice within a short period of time (exactly how long is platform-dependent).
+
+
+
+ Indicates that the mouse has moved while within this box, or that
+ the mouse while outside this box **if a button was pressed while within this box and has not yet been released**
+
+
+
+
+ A string is written to this property when a key is pressed or
+ released If the key was any other key, a multi-character
+ string describing the key will be put. For simplicity, we use
+ the VK_ constants in the . When a
+ key is pressed or released, the string put will be the portion
+ of its VK_ constant after the underscore, all in lower case.
+
+
+ If the shift key was depressed immediately before the
+ event took place, then the string will be capitalized. Special
+ keynames are also capitalized; shift+home is reported as
+ "[[HOME]]". Symbols are capitalized as they appear on the
+ keyboard; for example, on an American QWERTY keyboard, shift+2
+ is reported as "[[@]]".
+
+ If the alt, meta, or command key was depressed immediately
+ before this key was pressed, then the string will be prefixed
+ with the string "[[A-]]". If the control key was depressed
+ while this key was pressed, then the string will be prefixed
+ with the string "[[C-]]". If both alt and control are
+ depressed, the string is prefixed with "[[C-A-]]".
+
+ Ibex does not distinguish between a key press resulting from
+ the user physically pushing down a key, and a 'key press'
+ resulting from the keyboard's typematic repeat. In the rare
+ case that an application needs to distinguish between these
+ two events, it should watch for KeyReleased messages and
+ maintain an internal key-state vector.
+
+
+
+
+
+
+
+
+
+
+
+
+ XML-RPC objects can be created by calling [[ibex.net.rpc.xml(**XML-RPC
+ URL**)]], and then invoking methods on that object. For example,
+
+
+
+ When the user clicks the first mouse button on this box, it will
+ contact the server [[xmlrpc.ibex.org]], route to the
+ [[/RPC2/]] handler and invoke the [[getTodaysColor()]]
+ method on the [[color]] object with a single string argument
+ "[[Friday]]". The return value will be used to change the color
+ of the box the user clicked on.
+
+ Note that in this example we spawned a background thread to handle the
+ request -- the [[Press1]] event is delivered in the foreground
+ thread, and XML-RPC methods may only be invoked in background
+ threads. This is to prevent the UI from "locking up" if the server
+ takes a long time to reply.
+
+ If the XML-RPC method faults, an object will be thrown with two
+ properties: [[faultCode]] and [[faultString]], as defined in
+ the . If
+ Ibex encounters a network, transport, or session-layer error, it will
+ throw a [[String]] object describing the error in a
+ human-readable format. Scripts should not rely on the contents of
+ this string having any special structure or significance.
+
+ If an object with an associated non-empty stream is passed as an
+ argument to an XML-RPC method, it will be sent as a
+ element. If a element is found in the XML-RPC reply, it
+ will be returned as an object with a stream drawn from that byte sequence.
+
+ Each object returned by [[ibex.net.rpc.xml()]] represents a
+ single HTTP connection. The connection will be held open until
+ the object is garbage collected or the server closes the
+ connection. If a second call is issued on the object before the
+ first one returns (usually from a seperate thread), the two calls
+ will be . This can dramatically improve performance.
+
+ Ibex supports HTTP Basic and Digest authentication. To use
+ authentication, pass [[ibex.net.rpc.xml()]] a URL in the form
+
+
+ http[s]://user:password@hostname/
+
+
+ Ibex will use Digest authentication if the server supports it;
+ otherwise it will use Basic authentication. Please be aware that
+ many XML-RPC server implementations contain a .
+
+
+
+
+
+ SOAP methods are invoked the same way as XML-RPC methods, but with three differences:
+
+
+
+ [[ibex.net.rpc.soap()]] is used instead of
+ [[ibex.net.rpc.xml()]]
+
+ Instead of specifying just the URL of the service itself, you
+ must specify the URL, the SOAPAction argument, and the
+ namespace to use.
+
+ The actual method invocation takes only one argument, which
+ must be an object. This is necessary since SOAP arguments are
+ specified by name, rather than ordering.
+
+
+
+ SOAP faults are handled the same way as XML-RPC faults except that the
+ capitalization of the [[faultstring]] and [[faultcode]]
+ members is all lower-case, to match the SOAP spec. Here is a
+ SOAP example:
+
+
+
+ As you can see, SOAP is much more verbose, yet does not offer
+ substantially improved functionality. We recommend that XML-RPC be
+ used whenever possible, and that SOAP be reserved for legacy
+ applications.
+
+ The current Ibex SOAP stack does not support 'document style' or
+ multi-ref ([[href]]) data structures.
+
+
+
+
+
+ Applications downloaded from the network (as opposed to those loaded
+ from the filesystem) may only make certain kinds of connections to
+ certain hosts. See Appendix A for a detailed description of the
+ security policy.
+
+
+
+
+
+
+
+ If the Ibex Core encounters an error while servicing a function call
+ originating in JavaScript, the core will throw a string consisting of
+ an error code followed by a colon, a space, and a descriptive message.
+ For example:
+
+
+ "ibex.net.dns.unknownhostexception: unable to resolve host foo.com"
+
+
+ The code should be used to determine how the program should respond to
+ an error. The codes are organized in a hierarchy, so the
+ string.startsWith() method can be used to determine if an error lies
+ within a particular subhierarchy. The descriptive message portion of
+ the string may be shown to the user.
+
+
+ an assertion failed
+
+
+ General I/O exceptions
+
+
+ Error translating between character encodings.
+
+
+ Attempted to access a corrupt zip archive.
+
+
+ End of file encountered unexpectedly
+
+
+ A piece of untrusted Ibex code attempted to contact a
+ restricted host. See for details.
+
+
+ An attempt to resolve a hostname failed but it is not known
+ for certain that the hostname is invalid.
+
+
+ An attempt to resolve a hostname failed because the hostname
+ was invalid.
+
+
+ A socket was closed unexpectedly.
+
+
+ A connection could not be made to the remote host.
+
+
+ Tried to parse a malformed URL.
+
+
+ General SSL protocol errors.
+
+
+ The server's certificate was not signed by a CA trusted by Ibex.
+
+
+ Thrown when an HTTP error code is returned during an
+ operation. The three characters [[**xyz**]] will be
+ the three-digit HTTP status code.
+
+
+ The caller attempted to transmit the [[null]] value via XML-RPC.
+
+
+ The caller attempted to transmit a circular data structure via XML-RPC.
+
+
+ The caller attempted to transmit a "special" object via
+ XML-RPC (for example, a Box or the Ibex object).
+
+
+ A JavaScript attempted to put to a property on the [[null]] value
+
+
+ A JavaScript attempted to get from a property on the [[null]] value
+
+
+ A JavaScript attempted to call the [[null]] value
+
+
+ If an exception is thrown inside a trap, the exception will propagate
+ to the script that triggered the trap.
+
+ If an uncaught exception is thrown while applying a template, or the
+ requested template could not be found, an error will be logged and the
+ box to which the template was being applied will be made invisible
+ ([[visible = false]]). This ensures that half-applied widgets are
+ never shown to the user.
+
+
+
+
+
+
+
+
+ At any point in the Event Context, you can write to the [[mouse]]
+ property on any box. The value written should be an object with two
+ properties, [[x]] and [[y]]. For example:
+
+
+
+ The coordinates specified are relative to the box whose [[mouse]]
+ property is being written to. There is no need to supply the
+ [[inside]] property; it is computed automatically. Writing to
+ the [[mouse]] property causes Ibex to recompute the eventual
+ target box, and also alter the values returned by [[mouse.x]],
+ [[mouse.y]], and [[mouse.inside]] for any **descendants**
+ of the current box. Writing to the [[mouse]] property also
+ automatically prevents the event from returning to the box's parents
+ -- it is equivalent to not cascading on the non-underscored event.
+ This ensures that child boxes cannot trick their parent boxes into
+ thinking that the mouse has moved.
+
+ If you want the event to "skip over" the boxes between the trapee
+ and the target, or if you want to re-route an event to a box which
+ is not a descendant of the current box, simply write the value to
+ the proper key on the target box.
+
+
+
+
+
+
+
+ You can create "fake events" by simply writing to the [[mouse]]
+ property and then writing a value to one of the underscored properties
+ on a box. This will have exactly the same effect as if the use had
+ actually pressed a key, clicked a button, or moved the mouse -- they
+ are indistinguishable.
+
+
+
+
+
+ When the core first starts up, it clones the [[ibex]] object,
+ creates a stream for the initial .ibex, and then places a trap on the
+ cloned [[ibex]] object so that its empty-string property returns
+ the .ibex stream. The cloned Ibex object is then passed as the third
+ (optional) argument to [[ibex.apply()]], making it the default
+ [[ibex]] object for the scripts that are executed as part of the
+ template instantiation.
+
+
+
+ Note that we called [[ibex.bless()]] on the stream before tacking
+ it on to the new Ibex object. The bless function returns a clone of
+ the object passed to it, with a few traps which are explained below.
+ Additionally, any sub-streams retrieved by accessing properties of the
+ blessed stream will also automatically be blessed (blessed streams are
+ **monadic**).
+
+ Blessing a stream serves three purposes:
+
+
+
+ Blessed clones always return the appropriate static block when
+ their empty property is accessed; this ensures that references
+ to the static blocks of other templates work properly.
+
+ Blessed substreams can return their parent stream by accessing
+ a hidden property which is reserved for internal use by Ibex.
+ This ensures that Ibex can automatically add filename
+ extensions where needed, according to the following rules:
+
+
+ If the stream is a template to be applied, the string
+ "[[.ibex]]" is appended.
+
+ If the stream is an image, the string "[[.png]]" is
+ appended. If no stream is found, "[[.jpeg]]" and
+ "[[.gif]]" are tried, in that order.
+
+ If the stream is an font, the string "[[.ttf]]" is
+ appended.
+
+
+ Every call to [[ibex.bless()]] returns a different object
+ (which happens to be a clone of the object passed to it) with
+ a completely separate set of static blocks.
+
+
+
+ Ibex can self-emulate by using [[ibex.clone()]] on the Ibex object;
+ this technique is very similar to the use of ClassLoaders in
+ Java. This is useful for a number of applications, including
+ debuggers, IDEs, sandboxing untrusted code, remote-control, and
+ others. For example:
+
+
+
+
+
+
+
+
+ Due to the expense and hassle imposed by the commercial PKI code
+ signing architecture, and the fact that it , Ibex user interfaces are
+ distributed as unsigned, untrusted code. As such, they are handled
+ very carefully by the Ibex Core, and assumed to be potentially
+ malicious.
+
+ Ibex's security architecture is divided into defenses against four
+ major classes of attacks:
+
+
+
+ Ibex user interfaces are run in an extremely restrictive sandbox. The
+ environment does not provide primitives for accessing any data outside
+ the Ibex core except via XML-RPC and SOAP calls. There are no
+ facilities to allow Ibex user interfaces to access the client's
+ operating system or to interact with other applications on the same
+ host (unless they provide a public XML-RPC or SOAP interface).
+ An Ibex script may only access a file on the user's hard disk if the
+ user explicitly chooses that file from an "open file" or "save file"
+ dialog. There is one exception to this rule: if all templates
+ currently loaded in the Ibex core originated from the local
+ filesystem, those templates can load additional .ibexs from the local
+ filesystem.
+
+ The Ibex Core is written in Java, so it is not possible for
+ scripts to perform buffer overflow attacks against the core
+ itself.
+
+ Ibex applications may only read from the clipboard when the user
+ middle-clicks (X11 paste), presses control-V (Windows paste), or
+ presses alt-V (Macintosh paste; the command key is mapped to Ibex
+ "alt"). This ensures that Ibex applications are only granted access to
+ data that other applications have placed on the clipboard when the user
+ specifically indicates that that information should be made available
+ to the Ibex application.
+
+
+
+ Ibex user interfaces may only make XML-RPC or SOAP calls and load .ibex
+ archives via HTTP; they cannot execute arbitrary HTTP GET's or open
+ regular TCP sockets.
+
+ Ibex will not allow a script to connect to a non-public IP address
+ (10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16, as specified in ). There is one exception -- if all templates currently loaded
+ in the core originated from the same IP address, those scripts may
+ make calls to that IP address regardless of whether or not it is
+ firewalled. If Ibex does not have access to a DNS resolver (because it
+ is using a proxy which performs DNS lookups), Ibex will provide the
+ proxy with the appropriate header that the proxy needs in order
+ to maintain security.
+
+ The only remaining possible attack is against a XML-RPC or SOAP
+ service running on a firewalled host with a public address. Assigning
+ such machines public IP addresses is a poor network security policy,
+ and doing so squanders scarce public IPv4 addresses. As such, the onus
+ is on the administrators of such machines to explicitly block access
+ to clients reporting a [[User-Agent:]] header beginning with the
+ four characters "[[IBEX]]".
+
+
+
+ All top-level windows created by Ibex are **scarred** -- a stripe
+ and a lock is drawn across the corner of the window. There is no way
+ for a user interface to remove this scar. Ibex user interfaces may not
+ create windows smaller than the size of the scar.
+
+
+
+ Ibex user interfaces may transmit network data using HTTPS (SSL 3.0)
+ for encryption. Ibex will attempt 128-bit encryption, but will
+ negotiate down to 40-bit if the server does not support strong
+ crypto. Ibex's SSL implementation is currently provided by and
+
+ All HTTPS connections must be authenticated by the server using a
+ certificate whose name matches the domain name of the HTTPS URL. The
+ certificate must be signed by a trusted root CA. Ibex trusts the same
+ 93 root CAs whose certificates are included as "trusted" in Microsoft
+ Internet Explorer 5.5 SP2. This provides a minimal level of protection
+ against man-in-the-middle attacks; you should not trust this
+ connection with any data you would not normally trust an SSL-enabled
+ web browser with.
+
+
+
+
+
+
+ Ibex's scripts are written in a modified subset of ECMA-262, revision 3
+ (aka JavaScript 1.5). The interpreter strays from the spec in a few
+ ways.
+
+
+
+ The following ECMA features are not supported:
+
+
+
+ The [[undefined]] value, [[===]], and [[!==]]
+
+ The [[new]] keyword (and ECMAScript object inheritance)
+ [[eval]]
+
+ [[getter]] and [[setter]]
+
+ The ECMA [[this]] keyword.
+
+ The [[String]], [[Number]], and [[Boolean]]
+ classes. Note that [[string]], [[number]], and
+ [[boolean]] values are supported, however.
+
+ You may not [[throw]] the [[null]] value.
+
+
+
+ Additionally, you must declare all root-scope variables (with
+ [[var]]) before using them; failure to do so will result in an
+ exception. Box properties are pre-defined in the scope that scripts
+ are executed in.
+
+
+
+
+
+
+
+ The token [[..]] is equivalent to [[[""]]].
+
+ Trapping
+
+ Cloning
+
+ Extended [[catch]] syntax. The following code:
+
+ } catch(e) {
+ if (e.propname != null and e.propname >= "foo.bar.baz" and
+ "foo.bar.baz/" > e.propname) {
+ // ...
+ }
+ }
+
+ Multiple extended-catch blocks can appear at the end of a single try
+ block. However, at most one non-extended catch block may appear, and
+ if it does appear, it must be the last one.
+
+ Since Ibex ECMAscripts are wrapped in XML, the lexical token
+ "[[lt]]" is be interpreted as [[<]], the lexical
+ token "[[gt]]" is be interpreted as [[>]], and the
+ token "[[and]]" is interpreted as [[&&]].
+ Thus these tokens cannot be used as variable names.
+
+ The identifier [[static]] is a reserved word in
+ ECMAScript, but not in Ibex.
+
+ Ibex defines an additional reserved word, "[[assert]]",
+ which will evaluate the expression which follows it, throwing
+ a [[ibex.assertion.failed]] exception if the expression
+ evaluates to [[false]].
+
+ To ensure that Ibex files appear the same in all text editors, tab
+ characters are not allowed in Ibex files.
+
+
+
+ Some useful tutorials include:
+
+
+
+ Netscape's . Although this document is
+ out of date, it is arguably the best guide available for free
+ on the Internet. The changes from JavaScript 1.2 (aka ECMA-262
+ r1) to 1.5 were minimal, and many of them were from Ibex.
+
+ O'Reilly's , by David Flanagan
+ and Paula Ferguson. The latest edition of this book covers
+ JavaScript 1.5 (ECMA-262 r3).
+
+ The official specification. This is an extremely
+ technical document.
+
+
+
+
+
+
+
+
+
+ Usage: ibex [-lawp] [ url | file | directory ]
+ -l [level] set logging level to { debug, info (default), warn, error, silent }
+ -l rpc log all XML-RPC and SOAP conversations
+ -l user@host email log to user@host
+ -l host:port emit log to TCP socket
+ -l [file] write log to a file on disk
+ -a check assertions
+ -w [window-id] reserved for libibex
+ -p dump profiling information [not yet supported]
+
+
+ If Ibex encounters a serious problem before it starts logging
+ information, or if it is unable to open the log file, it will abort
+ immediately with a critical abort, which will be displayed on the
+ console for POSIX-native cores and in a dialog box for JVM-based and
+ Win32-native cores.
+
+ Note that Microsoft Windows does not provide any mechanism for
+ redirecting standard input/output of an application which is not
+ compiled as a "console application". Therefore, Ibex is compiled
+ as a "console application", and will open a console window when
+ invoked. To inhibit this console window, provide a logging
+ destination (file, port, etc).
+
+ The [[**source-location**]] parameter can be either the path
+ to an [[.ibex]] archive, the http url of an [[.ibex]]
+ archive, or the path to a directory comprising an unpacked
+ [[.ibex]] archive.
+
+ The [[**initial-template**]] parameter is the stream name of
+ a template to be used as the initial template. If ommitted, it
+ defaults to [[main]].
+
+ The [[-v]] option causes Ibex to enable verbose logging; this will
+ cause it to log **lots** of information to the log file. This
+ option will also substantially decrease Ibex's performance.
+
+
+
+
+
+
+**
+Copyright (C) 2004 Adam Megacz, verbatim redistribution permitted.
+Ibex is a trademark of Adam Megacz
+**
+
+
\ No newline at end of file
diff --git a/upstream/org.ibex.core/doc/reference/threeviews.pdf b/upstream/org.ibex.core/doc/reference/threeviews.pdf
new file mode 100644
index 0000000..0840c40
Binary files /dev/null and b/upstream/org.ibex.core/doc/reference/threeviews.pdf differ
diff --git a/upstream/org.ibex.core/next.build b/upstream/org.ibex.core/next.build
new file mode 100644
index 0000000..12d92eb
--- /dev/null
+++ b/upstream/org.ibex.core/next.build
@@ -0,0 +1 @@
+07F4
diff --git a/upstream/org.ibex.core/src/org/ibex/core/Box.java b/upstream/org.ibex.core/src/org/ibex/core/Box.java
new file mode 100644
index 0000000..0e95a7e
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/core/Box.java
@@ -0,0 +1,943 @@
+// FIXME
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.core;
+
+// FIXME: are traps on x/y meaningful?
+// FIXME: if we trap on cols, then set rows to 0 (forcing cols to 1), does the cols trap get triggered?
+// FIXME: if we change min{width/height}, thereby forcing a change to max{min/height}, does a trap on those get triggered?
+// FIXME: trap on numchildren? replaces ChildChanged?
+// FIXME: trap on visible, trigger when parent visibility changes
+
+// FIXME: ax/ay nonsense
+// FIXME: mouse move/release still needs to propagate to boxen in which the mouse was pressed and is still held down
+
+// FEATURE: mark to reflow starting with a certain child
+// FEATURE: reintroduce surface.abort
+
+import java.util.*;
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.graphics.*;
+
+/**
+ *
+ * Encapsulates the data for a single Ibex box as well as all layout
+ * rendering logic.
+ *
+ *
+ *
The rendering process consists of four phases; each requires
+ * one DFS pass over the tree
+ *
pack(): each box sets its childrens' row/col
+ *
constrain(): contentwidth is computed
+ *
resize(): width/height and x/y positions are set
+ *
render(): children draw their content onto the PixelBuffer.
+ *
+ * Implementations of the Platform class should return objects
+ * supporting this interface from the _createPixelBuffer()
+ * method. These implementations may choose to use off-screen video
+ * ram for this purpose (for example, a Pixmap on X11).
+ *
+ *
+ *
+ * Many of these functions come in pairs, one that uses ints and one
+ * that uses floats. The int functions are intended for situations
+ * in which the CTM is the identity transform.
+ *
+ */
+public abstract class PixelBuffer {
+
+ /** draw the picture at (dx1, dy1), cropping to (cx1, cy1, cx2, cy2) */
+ public abstract void drawPicture(Picture source, int dx1, int dy1, int cx1, int cy1, int cx2, int cy2);
+
+ /** fill a trapezoid whose top and bottom edges are horizontal */
+ public abstract void fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, int color);
+
+ /**
+ * Same as drawPicture, but only uses the alpha channel of the Picture, and is allowed to destructively modify the RGB
+ * channels of the Picture in the process. This method may assume that the RGB channels of the image are all zero IFF it
+ * restores this invariant before returning.
+ */
+ public abstract void drawGlyph(Font.Glyph source, int dx1, int dy1, int cx1, int cy1, int cx2, int cy2, int rgb);
+
+ // FEATURE: we want floats (inter-pixel spacing) for antialiasing, but this hoses the fastpath line drawing... argh!
+ /** draws a line of width w; note that the coordinates here are post-transform */
+ public void drawLine(int x1, int y1, int x2, int y2, int w, int color, boolean capped) {
+
+ if (y1 > y2) { int t = x1; x1 = x2; x2 = t; t = y1; y1 = y2; y2 = t; }
+
+ if (x1 == x2) {
+ fillTrapezoid(x1 - w / 2, x2 + w / 2, y1 - (capped ? w / 2 : 0),
+ x1 - w / 2, x2 + w / 2, y2 + (capped ? w / 2 : 0), color);
+ return;
+ }
+
+ // fastpath for single-pixel width lines
+ if (w == 1) {
+ float slope = (float)(y2 - y1) / (float)(x2 - x1);
+ int last_x = x1;
+ for(int y=y1; y<=y2; y++) {
+ int new_x = (int)((float)(y - y1) / slope) + x1;
+ if (slope >= 0) fillTrapezoid(last_x + 1, y != y2 ? new_x + 1 : new_x, y,
+ last_x + 1, y != y2 ? new_x + 1 : new_x, y + 1, color);
+ else fillTrapezoid(y != y2 ? new_x : new_x + 1, last_x, y,
+ y != y2 ? new_x : new_x + 1, last_x, y + 1, color);
+ last_x = new_x;
+ }
+ return;
+ }
+
+ // actually half-width
+ float width = (float)w / 2;
+ float phi = (float)Math.atan((y2 - y1) / (x2 - x1));
+ if (phi < 0.0) phi += (float)Math.PI * 2;
+ float theta = (float)Math.PI / 2 - phi;
+
+ // dx and dy are the x and y distance between each endpoint and the corner of the stroke
+ int dx = (int)(width * Math.cos(theta));
+ int dy = (int)(width * Math.sin(theta));
+
+ // slice is the longest possible length of a horizontal line across the stroke
+ int slice = (int)(2 * width / Math.cos(theta));
+
+ if (capped) {
+ x1 -= width * Math.cos(phi);
+ x2 += width * Math.cos(phi);
+ y1 -= width * Math.sin(phi);
+ y2 += width * Math.sin(phi);
+ }
+
+ fillTrapezoid(x1 + dx, x1 + dx, y1 - dy, x1 - dx, x1 - dx + slice, y1 + dy, color); // top corner
+ fillTrapezoid(x2 + dx - slice, x2 + dx, y2 - dy, x2 - dx, x2 - dx, y2 + dy, color); // bottom corner
+ fillTrapezoid(x1 - dx, x1 - dx + slice, y1 + dy, x2 + dx - slice, x2 + dx, y2 - dy, color); // middle
+ }
+
+}
diff --git a/upstream/org.ibex.core/src/org/ibex/graphics/SVG.java b/upstream/org.ibex.core/src/org/ibex/graphics/SVG.java
new file mode 100644
index 0000000..90311a6
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/graphics/SVG.java
@@ -0,0 +1,301 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.graphics;
+import java.util.*;
+
+
+// FIXME: offer a "subpixel" mode where we pass floats to the Platform and don't do any snapping
+// FIXME: fracture when realizing instead of when parsing?
+
+/*
+ v1.0
+ - textpath
+ - gradients
+ - patterns
+ - clipping/masking
+ - filters (filtering of a group must be performed AFTER the group is assembled; sep. canvas)
+
+ v1.1
+ - bump caps [requires Paint that can fill circles...] [remember to distinguish between closed/unclosed]
+ - line joins
+ - mitre (hard)
+ - bevel (easy)
+ - bump (easy, but requires 'round' Paint)
+ - subtree sharing? otherwise the memory consumption might be outrageous... clone="" attribute?
+ - better clipping
+ - intersect clip regions (linearity)
+ - clip on trapezoids, not pixels
+ - faster gradients and patterns:
+ - transform each corner of the trapezoid and then interpolate
+*/
+
+// FIXME: need to support style sheets and the 'style=' attribute
+// FIXME: need to convert markers into subboxes
+public class SVG {
+
+ /*
+ public static void parseNode(String name, String[] keys, Object[] vals, Template t) {
+ Hash h = new Hash();
+ for(int i=0; iname property * /
+ Hashtable glyphByName = new Hashtable();
+
+ / ** linked list of glyphs, stored by the first character of their unicode property * /
+ Hashtable glyphByUnicode = new Hashtable();
+
+ // a Glyph in an VectorGraphics font
+ public static class Glyph {
+
+ // FIXME: lang attribute
+ boolean isVerticallyOriented = false;
+ Template t = null;
+ Box b = null;
+
+ float horiz_adv_x = 0;
+ float vert_origin_x = 0;
+ float vert_origin_y = 0;
+ float vert_adv_y = 0;
+
+ String unicode = null;
+
+ // forms the linked list in glyphByUnicode; glyphs appear in the order specified in the font
+ public Glyph next = null;
+
+ Glyph(String name, String unicode, Template t, VectorGraphics.Font f) {
+ if (unicode != null)
+ if (f.glyphByUnicode.get(unicode.substring(0, 1)) == null) {
+ f.glyphByUnicode.put(unicode.substring(0, 1), this);
+ } else {
+ Glyph g;
+ for(g = (Glyph)f.glyphByUnicode.get(unicode.substring(0, 1)); g.next != null; g = g.next);
+ g.next = this;
+ }
+ if (name != null) f.glyphByUnicode.put(name, this);
+ this.unicode = unicode;
+ this.t = t;
+ horiz_adv_x = f.horiz_adv_x;
+ vert_origin_x = f.vert_origin_x;
+ vert_origin_y = f.vert_origin_y;
+ vert_adv_y = f.vert_adv_y;
+ }
+ public void render(DoubleBuffer buf, int x, int y, int fillcolor, int strokecolor, float scaleFactor) {
+ // FEATURE: make b double-buffered for increased performance
+ if (b == null) {
+ b = new Box(t, new org.ibex.util.Vec(), new org.ibex.util.Vec(), null, 0, 0);
+ b.put("absolute", Boolean.TRUE);
+ b.prerender();
+ t = null;
+ }
+ // FIXME
+ b.put("width", new Integer(1000));
+ b.put("height", new Integer(1000));
+ b.fillcolor = fillcolor;
+ b.strokecolor = strokecolor;
+
+ // we toss an extra flip on the ctm so that fonts stick "up" instead of down
+ b.render(0, 0, buf.getWidth(), buf.getHeight(), buf,
+ Affine.flip(false, true).multiply(Affine.scale(scaleFactor, scaleFactor).multiply(Affine.translate(x, y))).multiply(buf.a));
+ }
+ }
+ }
+ */
+
+}
diff --git a/upstream/org.ibex.core/src/org/ibex/graphics/Surface.java b/upstream/org.ibex.core/src/org/ibex/graphics/Surface.java
new file mode 100644
index 0000000..31ac36e
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/graphics/Surface.java
@@ -0,0 +1,405 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.graphics;
+
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.plat.*;
+
+import org.ibex.core.*; // FIXME
+
+/**
+ * A Surface, as described in the Ibex Reference.
+ *
+ * Platform subclasses should include an inner class subclass of
+ * Surface to return from the Platform._createSurface() method
+ */
+public abstract class Surface extends PixelBuffer implements Task {
+
+ // Static Data ////////////////////////////////////////////////////////////////////////////////
+
+ private static Boolean T = Boolean.TRUE;
+ private static Boolean F = Boolean.FALSE;
+
+ /** all instances of Surface which need to be refreshed by the Scheduler */
+ public static Vec allSurfaces = new Vec();
+
+ /** When set to true, render() should abort as soon as possible and restart the rendering process */
+ public volatile boolean abort = false;
+
+ // these three variables are used to ensure that user resizes trump programmatic resizes
+ public volatile boolean syncRootBoxToSurface = false;
+ public volatile int pendingWidth = 0;
+ public volatile int pendingHeight = 0;
+
+ public static boolean alt = false; ///< true iff the alt button is pressed down
+ public static boolean control = false; ///< true iff the control button is pressed down
+ public static boolean shift = false; ///< true iff the shift button is pressed down
+ public static boolean button1 = false; ///< true iff button 1 is depressed
+ public static boolean button2 = false; ///< true iff button 2 is depressed
+ public static boolean button3 = false; ///< true iff button 3 is depressed
+
+
+ // Instance Data ///////////////////////////////////////////////////////////////////////
+
+ public Box root; ///< The Box at the root of this surface
+ public String cursor = "default"; ///< The active cursor to switch to when syncCursor() is called
+ public int mousex; ///< x position of the mouse
+ public int mousey; ///< y position of the mouse
+ public int _mousex; ///< x position of the mouse FIXME
+ public int _mousey; ///< y position of the mouse FIXME
+ public int newmousex = -1; ///< x position of the mouse, in real time; this lets us collapse Move's
+ public int newmousey = -1; ///< y position of the mouse, in real time; this lets us collapse Move's
+ public boolean minimized = false; ///< True iff this surface is minimized, in real time
+ public boolean maximized = false; ///< True iff this surface is maximized, in real time
+ public boolean unrendered = true; ///< True iff this surface has not yet been rendered
+ DirtyList dirtyRegions = new DirtyList(); ///< Dirty regions on the surface
+
+ // Used For Simulating Clicks and DoubleClicks /////////////////////////////////////////////////
+
+ int last_press_x = Integer.MAX_VALUE; ///< the x-position of the mouse the last time a Press message was enqueued
+ int last_press_y = Integer.MAX_VALUE; ///< the y-position of the mouse the last time a Press message was enqueued
+ static int lastClickButton = 0; ///< the last button to recieve a Click message; used for simulating DoubleClick's
+ static long lastClickTime = 0; ///< the last time a Click message was processed; used for simulating DoubleClick's
+
+
+ // Methods to be overridden by subclasses ///////////////////////////////////////////////////////
+
+ public abstract void toBack(); ///< should push surface to the back of the stacking order
+ public abstract void toFront(); ///< should pull surface to the front of the stacking order
+ public abstract void syncCursor(); ///< set the actual cursor to this.cursor if they do not match
+ public abstract void setInvisible(boolean b); ///< If b, make window invisible; otherwise, make it non-invisible.
+ protected abstract void _setMaximized(boolean b); ///< If b, maximize the surface; otherwise, un-maximize it.
+ protected abstract void _setMinimized(boolean b); ///< If b, minimize the surface; otherwise, un-minimize it.
+ public abstract void setLocation(); ///< Set the surface's x/y position to that of the root box
+ protected abstract void _setSize(int w, int h); ///< set the actual size of the surface
+ public abstract void setTitleBarText(String s); ///< Sets the surface's title bar text, if applicable
+ public abstract void setIcon(Picture i); ///< Sets the surface's title bar text, if applicable
+ public abstract void _dispose(); ///< Destroy the surface
+ public void setMinimumSize(int minx, int miny, boolean resizable) { }
+ protected void setSize(int w, int h) { _setSize(w, h); }
+
+ public static Picture scarImage = null;
+
+ // Helper methods for subclasses ////////////////////////////////////////////////////////////
+
+ protected final void Press(final int button) {
+ last_press_x = mousex;
+ last_press_y = mousey;
+
+ if (button == 1) button1 = true;
+ else if (button == 2) button2 = true;
+ else if (button == 3) button3 = true;
+
+ if (button == 1) new Message("_Press1", T, root);
+ else if (button == 2) new Message("_Press2", T, root);
+ else if (button == 3) {
+ Scheduler.add(new Task() { public void perform() throws JSExn {
+ Platform.clipboardReadEnabled = true;
+ try {
+ root.putAndTriggerTraps("_Press3", T);
+ } finally {
+ Platform.clipboardReadEnabled = false;
+ }
+ }});
+ }
+ }
+
+ protected final void Release(int button) {
+ if (button == 1) button1 = false;
+ else if (button == 2) button2 = false;
+ else if (button == 3) button3 = false;
+
+ if (button == 1) new Message("_Release1", T, root);
+ else if (button == 2) new Message("_Release2", T, root);
+ else if (button == 3) new Message("_Release3", T, root);
+
+ if (Platform.needsAutoClick() && Math.abs(last_press_x - mousex) < 5 && Math.abs(last_press_y - mousey) < 5) Click(button);
+ last_press_x = Integer.MAX_VALUE;
+ last_press_y = Integer.MAX_VALUE;
+ }
+
+ protected final void Click(int button) {
+ if (button == 1) new Message("_Click1", T, root);
+ else if (button == 2) new Message("_Click2", T, root);
+ else if (button == 3) new Message("_Click3", T, root);
+ if (Platform.needsAutoDoubleClick()) {
+ long now = System.currentTimeMillis();
+ if (lastClickButton == button && now - lastClickTime < 350) DoubleClick(button);
+ lastClickButton = button;
+ lastClickTime = now;
+ }
+ }
+
+ /** we enqueue ourselves in the Scheduler when we have a Move message to deal with */
+ private Task mover = new Task() {
+ public void perform() {
+ if (mousex == newmousex && mousey == newmousey) return;
+ int oldmousex = mousex; mousex = newmousex;
+ int oldmousey = mousey; mousey = newmousey;
+ String oldcursor = cursor; cursor = "default";
+ // FIXME: Root (ONLY) gets motion events outside itself (if trapped)
+ if (oldmousex != mousex || oldmousey != mousey)
+ root.putAndTriggerTrapsAndCatchExceptions("_Move", T);
+ if (!cursor.equals(oldcursor)) syncCursor();
+ } };
+
+ /**
+ * Notify Ibex that the mouse has moved. If the mouse leaves the
+ * surface, but the host windowing system does not provide its new
+ * position (for example, a Java MouseListener.mouseExited()
+ * message), the subclass should use (-1,-1).
+ */
+ protected final void Move(final int newmousex, final int newmousey) {
+ this.newmousex = newmousex;
+ this.newmousey = newmousey;
+ Scheduler.add(mover);
+ }
+
+ protected final void HScroll(int pixels) { new Message("_HScroll", new Integer(pixels), root); }
+ protected final void VScroll(int pixels) { new Message("_VScroll", new Integer(pixels), root); }
+ protected final void HScroll(float lines) { new Message("_HScroll", new Float(lines), root); }
+ protected final void VScroll(float lines) { new Message("_VScroll", new Float(lines), root); }
+
+ /** subclasses should invoke this method when the user resizes the window */
+ protected final void SizeChange(final int width, final int height) {
+ if (unrendered || (pendingWidth == width && pendingHeight == height)) return;
+ pendingWidth = width;
+ pendingHeight = height;
+ syncRootBoxToSurface = true;
+ abort = true;
+ Scheduler.renderAll();
+ }
+
+ // FEATURE: can we avoid creating objects here?
+ protected final void PosChange(final int x, final int y) {
+ Scheduler.add(new Task() { public void perform() throws JSExn {
+ root.x = x;
+ root.y = y;
+ root.putAndTriggerTrapsAndCatchExceptions("PosChange", T);
+ }});
+ }
+
+ private final String[] doubleClick = new String[] { null, "_DoubleClick1", "_DoubleClick2", "_DoubleClick3" };
+ protected final void DoubleClick(int button) { new Message(doubleClick[button], T, root); }
+ protected final void KeyPressed(String key) { new Message("_KeyPressed", key, root); }
+ protected final void KeyReleased(String key) { new Message("_KeyReleased", key, root); }
+ protected final void Close() { new Message("Close", T, root); }
+ protected final void Minimized(boolean b) { minimized = b; new Message("Minimized", b ? T : F, root); }
+ protected final void Maximized(boolean b) { maximized = b; new Message("Maximized", b ? T : F, root); }
+ protected final void Focused(boolean b) { new Message("Focused", b ? T : F, root); }
+
+ private boolean scheduled = false;
+ public void Refresh() { if (!scheduled) Scheduler.add(this); scheduled = true; }
+ public void perform() { scheduled = false; Scheduler.renderAll(); }
+
+ public final void setMaximized(boolean b) { if (b != maximized) _setMaximized(maximized = b); }
+ public final void setMinimized(boolean b) { if (b != minimized) _setMinimized(minimized = b); }
+
+
+ // Other Methods ///////////////////////////////////////////////////////////////////////////////
+
+ /** Indicates that the Surface is no longer needed */
+ public final void dispose(boolean quitIfAllSurfacesGone) {
+ if (Log.on) Log.info(this, "disposing " + this);
+ allSurfaces.removeElement(this);
+ _dispose();
+ if (allSurfaces.size() == 0) {
+ if (Log.on) Log.info(this, "exiting because last surface was destroyed");
+ System.exit(0);
+ }
+ }
+
+ public void dirty(int x, int y, int w, int h) {
+ dirtyRegions.dirty(x, y, w, h);
+ Refresh();
+ }
+
+ public static Surface fromBox(Box b) {
+ // FIXME use a hash table here
+ for(int i=0; i root.width) w = root.width - x;
+ if (y+h > root.height) h = root.height - y;
+ if (w <= 0 || h <= 0) continue;
+
+ root.render(0, 0, x, y, x + w, y + h, this, identity);
+ drawPicture(scarImage, 0, root.height - scarImage.height, x, y, x+w, y+h);
+
+ if (abort) {
+ // x,y,w,h is only partially reconstructed, so we must be careful not to re-blit it
+ dirtyRegions.dirty(x, y, w, h);
+ // put back all the dirty regions we haven't yet processed (including the current one)
+ for(int j=i; j root.width) w = root.width - x;
+ if (y+h > root.height) h = root.height - y;
+ if (w <= 0 || h <= 0) continue;
+ if (abort) return;
+ blit(backbuffer, x, y, x, y, w + x, h + y);
+ }
+ }
+
+ /** This is how subclasses signal a 'shallow dirty', indicating that although the backbuffer is valid, the screen is not */
+ public final void Dirty(int x, int y, int w, int h) {
+ screenDirtyRegions.dirty(x, y, w, h);
+ Scheduler.renderAll();
+ }
+
+ public void dirty(int x, int y, int w, int h) {
+ screenDirtyRegions.dirty(x, y, w, h);
+ super.dirty(x, y, w, h);
+ }
+
+ /** copies a region from the doublebuffer to this surface */
+ public abstract void blit(PixelBuffer source, int sx, int sy, int dx, int dy, int dx2, int dy2);
+
+ }
+
+}
diff --git a/upstream/org.ibex.core/src/org/ibex/js/ByteCodes.java b/upstream/org.ibex.core/src/org/ibex/js/ByteCodes.java
new file mode 100644
index 0000000..e8170f1
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/js/ByteCodes.java
@@ -0,0 +1,94 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+/**
+ * Constants for the various JavaScript ByteCode operations.
+ *
+ * Each instruction is an opcode and an optional literal literal;
+ * some Tokens are also valid; see Tokens.java
+ */
+interface ByteCodes {
+
+ /** push the literal onto the stack */
+ public static final byte LITERAL = -2;
+
+ /** push a new array onto the stack with length equal to the literal */
+ public static final byte ARRAY = -3;
+
+ /** push an empty object onto the stack */
+ public static final byte OBJECT = -4;
+
+ /** create a new instance; literal is a reference to the corresponding ForthBlock */
+ public static final byte NEWFUNCTION = -5;
+
+ /** if given a non-null argument declare its argument in the current scope and push
+ it to the stack, else, declares the element on the top of the stack and leaves it
+ there */
+ public static final byte DECLARE = -6;
+
+ /** push a reference to the current scope onto the stack */
+ public static final byte TOPSCOPE = -7;
+
+ /** if given a null literal pop two elements off the stack; push stack[-1].get(stack[top])
+ else pop one element off the stack, push stack[top].get(literal) */
+ public static final byte GET = -8;
+
+ /** push stack[-1].get(stack[top]) */
+ public static final byte GET_PRESERVE = -9;
+
+ /** pop two elements off the stack; stack[-2].put(stack[-1], stack[top]); push stack[top] */
+ public static final byte PUT = -10;
+
+ /** literal is a relative address; pop stacktop and jump if the value is true */
+ public static final byte JT = -11;
+
+ /** literal is a relative address; pop stacktop and jump if the value is false */
+ public static final byte JF = -12;
+
+ /** literal is a relative address; jump to it */
+ public static final byte JMP = -13;
+
+ /** discard the top stack element */
+ static public final byte POP = -14;
+
+ /** pop element; call stack[top](stack[-n], stack[-n+1]...) where n is the number of args to the function */
+ public static final byte CALL = -15;
+
+ /** pop an element; push a JS.JSArray containing the keys of the popped element */
+ public static final byte PUSHKEYS = -16;
+
+ /** push the top element down so that (arg) elements are on top of it; all other elements retain ordering */
+ public static final byte SWAP = -17;
+
+ /** execute the bytecode block pointed to by the literal in a fresh scope with parentScope==THIS */
+ public static final byte NEWSCOPE = -18;
+
+ /** execute the bytecode block pointed to by the literal in a fresh scope with parentScope==THIS */
+ public static final byte OLDSCOPE = -19;
+
+ /** push a copy of the top stack element */
+ public static final byte DUP = -20;
+
+ /** a NOP; confers a label upon the following instruction */
+ public static final byte LABEL = -21;
+
+ /** execute the ForthBlock pointed to by the literal until BREAK encountered; push TRUE onto the stack for the first iteration
+ * and FALSE for all subsequent iterations */
+ public static final byte LOOP = -22;
+
+ /** similar effect a a GET followed by a CALL */
+ public static final byte CALLMETHOD = -23;
+
+ /** finish a finally block and carry out whatever instruction initiated the finally block */
+ public static final byte FINALLY_DONE = -24;
+
+ /** finish a finally block and carry out whatever instruction initiated the finally block */
+ public static final byte MAKE_GRAMMAR = -25;
+
+ public static final String[] bytecodeToString = new String[] {
+ "", "", "LITERAL", "ARRAY", "OBJECT", "NEWFUNCTION", "DECLARE", "TOPSCOPE",
+ "GET", "GET_PRESERVE", "PUT", "JT", "JF", "JMP", "POP", "CALL", "PUSHKEYS",
+ "SWAP", "NEWSCOPE", "OLDSCOPE", "DUP", "LABEL", "LOOP", "CALLMETHOD",
+ "FINALLY_DONE", "MAKE_GRAMMAR"
+ };
+}
diff --git a/upstream/org.ibex.core/src/org/ibex/js/Interpreter.java b/upstream/org.ibex.core/src/org/ibex/js/Interpreter.java
new file mode 100644
index 0000000..14f0f19
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/js/Interpreter.java
@@ -0,0 +1,744 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+import org.ibex.util.*;
+import java.util.*;
+
+/** Encapsulates a single JS interpreter (ie call stack) */
+class Interpreter implements ByteCodes, Tokens {
+
+
+ // Thread-Interpreter Mapping /////////////////////////////////////////////////////////////////////////
+
+ static Interpreter current() { return (Interpreter)threadToInterpreter.get(Thread.currentThread()); }
+ private static Hashtable threadToInterpreter = new Hashtable();
+
+
+ // Instance members and methods //////////////////////////////////////////////////////////////////////
+
+ int pausecount; ///< the number of times pause() has been invoked; -1 indicates unpauseable
+ JSFunction f = null; ///< the currently-executing JSFunction
+ JSScope scope; ///< the current top-level scope (LIFO stack via NEWSCOPE/OLDSCOPE)
+ Vec stack = new Vec(); ///< the object stack
+ int pc = 0; ///< the program counter
+
+ Interpreter(JSFunction f, boolean pauseable, JSArray args) {
+ stack.push(new Interpreter.CallMarker(this)); // the "root function returned" marker -- f==null
+ this.f = f;
+ this.pausecount = pauseable ? 0 : -1;
+ this.scope = new JSScope(f.parentScope);
+ stack.push(args);
+ }
+
+ /** this is the only synchronization point we need in order to be threadsafe */
+ synchronized Object resume() throws JSExn {
+ Thread t = Thread.currentThread();
+ Interpreter old = (Interpreter)threadToInterpreter.get(t);
+ threadToInterpreter.put(t, this);
+ try {
+ return run();
+ } finally {
+ if (old == null) threadToInterpreter.remove(t);
+ else threadToInterpreter.put(t, old);
+ }
+ }
+
+ static int getLine() {
+ Interpreter c = Interpreter.current();
+ return c == null || c.f == null || c.pc < 0 || c.pc >= c.f.size ? -1 : c.f.line[c.pc];
+ }
+
+ static String getSourceName() {
+ Interpreter c = Interpreter.current();
+ return c == null || c.f == null ? null : c.f.sourceName;
+ }
+
+ private static JSExn je(String s) { return new JSExn(getSourceName() + ":" + getLine() + " " + s); }
+
+ // FIXME: double check the trap logic
+ private Object run() throws JSExn {
+
+ // if pausecount changes after a get/put/call, we know we've been paused
+ final int initialPauseCount = pausecount;
+
+ OUTER: for(;; pc++) {
+ try {
+ if (f == null) return stack.pop();
+ int op = f.op[pc];
+ Object arg = f.arg[pc];
+ if(op == FINALLY_DONE) {
+ FinallyData fd = (FinallyData) stack.pop();
+ if(fd == null) continue OUTER; // NOP
+ if(fd.exn != null) throw fd.exn;
+ op = fd.op;
+ arg = fd.arg;
+ }
+ switch(op) {
+ case LITERAL: stack.push(arg); break;
+ case OBJECT: stack.push(new JS()); break;
+ case ARRAY: stack.push(new JSArray(JS.toNumber(arg).intValue())); break;
+ case DECLARE: scope.declare((String)(arg==null ? stack.peek() : arg)); if(arg != null) stack.push(arg); break;
+ case TOPSCOPE: stack.push(scope); break;
+ case JT: if (JS.toBoolean(stack.pop())) pc += JS.toNumber(arg).intValue() - 1; break;
+ case JF: if (!JS.toBoolean(stack.pop())) pc += JS.toNumber(arg).intValue() - 1; break;
+ case JMP: pc += JS.toNumber(arg).intValue() - 1; break;
+ case POP: stack.pop(); break;
+ case SWAP: {
+ int depth = (arg == null ? 1 : JS.toInt(arg));
+ Object save = stack.elementAt(stack.size() - 1);
+ for(int i=stack.size() - 1; i > stack.size() - 1 - depth; i--)
+ stack.setElementAt(stack.elementAt(i-1), i);
+ stack.setElementAt(save, stack.size() - depth - 1);
+ break; }
+ case DUP: stack.push(stack.peek()); break;
+ case NEWSCOPE: scope = new JSScope(scope); break;
+ case OLDSCOPE: scope = scope.getParentScope(); break;
+ case ASSERT:
+ if (JS.checkAssertions && !JS.toBoolean(stack.pop()))
+ throw je("ibex.assertion.failed" /*FEATURE: line number*/); break;
+ case BITNOT: stack.push(JS.N(~JS.toLong(stack.pop()))); break;
+ case BANG: stack.push(JS.B(!JS.toBoolean(stack.pop()))); break;
+ case NEWFUNCTION: stack.push(((JSFunction)arg)._cloneWithNewParentScope(scope)); break;
+ case LABEL: break;
+
+ case TYPEOF: {
+ Object o = stack.pop();
+ if (o == null) stack.push(null);
+ else if (o instanceof JS) stack.push("object");
+ else if (o instanceof String) stack.push("string");
+ else if (o instanceof Number) stack.push("number");
+ else if (o instanceof Boolean) stack.push("boolean");
+ else throw new Error("this should not happen");
+ break;
+ }
+
+ case PUSHKEYS: {
+ Object o = stack.peek();
+ Enumeration e = ((JS)o).keys();
+ JSArray a = new JSArray();
+ while(e.hasMoreElements()) a.addElement(e.nextElement());
+ stack.push(a);
+ break;
+ }
+
+ case LOOP:
+ stack.push(new LoopMarker(pc, pc > 0 && f.op[pc - 1] == LABEL ? (String)f.arg[pc - 1] : (String)null, scope));
+ stack.push(Boolean.TRUE);
+ break;
+
+ case BREAK:
+ case CONTINUE:
+ while(stack.size() > 0) {
+ Object o = stack.pop();
+ if (o instanceof CallMarker) je("break or continue not within a loop");
+ if (o instanceof TryMarker) {
+ if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going
+ stack.push(new FinallyData(op, arg));
+ scope = ((TryMarker)o).scope;
+ pc = ((TryMarker)o).finallyLoc - 1;
+ continue OUTER;
+ }
+ if (o instanceof LoopMarker) {
+ if (arg == null || arg.equals(((LoopMarker)o).label)) {
+ int loopInstructionLocation = ((LoopMarker)o).location;
+ int endOfLoop = ((Integer)f.arg[loopInstructionLocation]).intValue() + loopInstructionLocation;
+ scope = ((LoopMarker)o).scope;
+ if (op == CONTINUE) { stack.push(o); stack.push(Boolean.FALSE); }
+ pc = op == BREAK ? endOfLoop - 1 : loopInstructionLocation;
+ continue OUTER;
+ }
+ }
+ }
+ throw new Error("CONTINUE/BREAK invoked but couldn't find LoopMarker at " +
+ getSourceName() + ":" + getLine());
+
+ case TRY: {
+ int[] jmps = (int[]) arg;
+ // jmps[0] is how far away the catch block is, jmps[1] is how far away the finally block is
+ // each can be < 0 if the specified block does not exist
+ stack.push(new TryMarker(jmps[0] < 0 ? -1 : pc + jmps[0], jmps[1] < 0 ? -1 : pc + jmps[1], this));
+ break;
+ }
+
+ case RETURN: {
+ Object retval = stack.pop();
+ while(stack.size() > 0) {
+ Object o = stack.pop();
+ if (o instanceof TryMarker) {
+ if(((TryMarker)o).finallyLoc < 0) continue;
+ stack.push(retval);
+ stack.push(new FinallyData(RETURN));
+ scope = ((TryMarker)o).scope;
+ pc = ((TryMarker)o).finallyLoc - 1;
+ continue OUTER;
+ } else if (o instanceof CallMarker) {
+ if (scope instanceof Trap.TrapScope) { // handles return component of a read trap
+ Trap.TrapScope ts = (Trap.TrapScope)scope;
+ if (retval != null && retval instanceof Boolean && ((Boolean)retval).booleanValue())
+ ts.cascadeHappened = true;
+ if (!ts.cascadeHappened) {
+ ts.cascadeHappened = true;
+ Trap t = ts.t.next;
+ while (t != null && t.f.numFormalArgs == 0) t = t.next;
+ if (t == null) {
+ ((JS)ts.t.trapee).put(ts.t.name, ts.val);
+ if (pausecount > initialPauseCount) { pc++; return null; } // we were paused
+ } else {
+ stack.push(o);
+ JSArray args = new JSArray();
+ args.addElement(ts.val);
+ stack.push(args);
+ f = t.f;
+ scope = new Trap.TrapScope(f.parentScope, t, ts.val);
+ pc = -1;
+ continue OUTER;
+ }
+ }
+ }
+ scope = ((CallMarker)o).scope;
+ pc = ((CallMarker)o).pc - 1;
+ f = (JSFunction)((CallMarker)o).f;
+ stack.push(retval);
+ continue OUTER;
+ }
+ }
+ throw new Error("error: RETURN invoked but couldn't find a CallMarker!");
+ }
+
+ case PUT: {
+ Object val = stack.pop();
+ Object key = stack.pop();
+ Object target = stack.peek();
+ if (target == null)
+ throw je("tried to put a value to the " + key + " property on the null value");
+ if (!(target instanceof JS))
+ throw je("tried to put a value to the " + key + " property on a " + target.getClass().getName());
+ if (key == null)
+ throw je("tried to assign \"" + (val==null?"(null)":val.toString()) + "\" to the null key");
+
+ Trap t = null;
+ if (target instanceof JSScope && key.equals("cascade")) {
+ Trap.TrapScope ts = null;
+ JSScope p = (JSScope)target; // search the scope-path for the trap
+ if (target instanceof Trap.TrapScope) {
+ ts = (Trap.TrapScope)target;
+ }
+ else {
+ while (ts == null && p.getParentScope() != null) {
+ p = p.getParentScope();
+ if (p instanceof Trap.TrapScope) {
+ ts = (Trap.TrapScope)p;
+ }
+ }
+ }
+ t = ts.t.next;
+ ts.cascadeHappened = true;
+ while (t != null && t.f.numFormalArgs == 0) t = t.next;
+ if (t == null) { target = ts.t.trapee; key = ts.t.name; }
+
+ } else if (target instanceof Trap.TrapScope && key.equals(((Trap.TrapScope)target).t.name)) {
+ throw je("tried to put to " + key + " inside a trap it owns; use cascade instead");
+
+ } else if (target instanceof JS) {
+ if (target instanceof JSScope) {
+ JSScope p = (JSScope)target; // search the scope-path for the trap
+ t = p.getTrap(key);
+ while (t == null && p.getParentScope() != null) { p = p.getParentScope(); t = p.getTrap(key); }
+ } else {
+ t = ((JS)target).getTrap(key);
+ }
+ while (t != null && t.f.numFormalArgs == 0) t = t.next; // find the first write trap
+ }
+ if (t != null) {
+ stack.push(new CallMarker(this));
+ JSArray args = new JSArray();
+ args.addElement(val);
+ stack.push(args);
+ f = t.f;
+ scope = new Trap.TrapScope(f.parentScope, t, val);
+ pc = -1;
+ break;
+ }
+ ((JS)target).put(key, val);
+ if (pausecount > initialPauseCount) { pc++; return null; } // we were paused
+ stack.push(val);
+ break;
+ }
+
+ case GET:
+ case GET_PRESERVE: {
+ Object o, v;
+ if (op == GET) {
+ v = arg == null ? stack.pop() : arg;
+ o = stack.pop();
+ } else {
+ v = stack.pop();
+ o = stack.peek();
+ stack.push(v);
+ }
+ Object ret = null;
+ if (v == null) throw je("tried to get the null key from " + o);
+ if (o == null) throw je("tried to get property \"" + v + "\" from the null object");
+ if (o instanceof String || o instanceof Number || o instanceof Boolean) {
+ ret = getFromPrimitive(o,v);
+ stack.push(ret);
+ break;
+ } else if (o instanceof JS) {
+ Trap t = null;
+ if (o instanceof Trap.TrapScope && v.equals("cascade")) {
+ t = ((Trap.TrapScope)o).t.next;
+ while (t != null && t.f.numFormalArgs != 0) t = t.next;
+ if (t == null) { v = ((Trap.TrapScope)o).t.name; o = ((Trap.TrapScope)o).t.trapee; }
+
+ } else if (o instanceof JS) {
+ if (o instanceof JSScope) {
+ JSScope p = (JSScope)o; // search the scope-path for the trap
+ t = p.getTrap(v);
+ while (t == null && p.getParentScope() != null) { p = p.getParentScope(); t = p.getTrap(v); }
+ } else {
+ t = ((JS)o).getTrap(v);
+ }
+ while (t != null && t.f.numFormalArgs != 0) t = t.next; // get first read trap
+ }
+ if (t != null) {
+ stack.push(new CallMarker(this));
+ JSArray args = new JSArray();
+ stack.push(args);
+ f = t.f;
+ scope = new Trap.TrapScope(f.parentScope, t, null);
+ ((Trap.TrapScope)scope).cascadeHappened = true;
+ pc = -1;
+ break;
+ }
+ ret = ((JS)o).get(v);
+ if (ret == JS.METHOD) ret = new Stub((JS)o, v);
+ if (pausecount > initialPauseCount) { pc++; return null; } // we were paused
+ stack.push(ret);
+ break;
+ }
+ throw je("tried to get property " + v + " from a " + o.getClass().getName());
+ }
+
+ case CALL: case CALLMETHOD: {
+ int numArgs = JS.toInt(arg);
+ Object method = null;
+ Object ret = null;
+ Object object = stack.pop();
+
+ if (op == CALLMETHOD) {
+ if (object == JS.METHOD) {
+ method = stack.pop();
+ object = stack.pop();
+ } else if (object == null) {
+ Object name = stack.pop();
+ stack.pop();
+ throw new JSExn("function '"+name+"' not found");
+ } else {
+ stack.pop();
+ stack.pop();
+ }
+ }
+ Object[] rest = numArgs > 3 ? new Object[numArgs - 3] : null;
+ for(int i=numArgs - 1; i>2; i--) rest[i-3] = stack.pop();
+ Object a2 = numArgs <= 2 ? null : stack.pop();
+ Object a1 = numArgs <= 1 ? null : stack.pop();
+ Object a0 = numArgs <= 0 ? null : stack.pop();
+
+ if (object instanceof String || object instanceof Number || object instanceof Boolean) {
+ ret = callMethodOnPrimitive(object, method, a0, a1, a2, null, numArgs);
+
+ } else if (object instanceof JSFunction) {
+ // FIXME: use something similar to call0/call1/call2 here
+ JSArray arguments = new JSArray();
+ for(int i=0; i initialPauseCount) { pc++; return null; }
+ stack.push(ret);
+ break;
+ }
+
+ case THROW:
+ throw new JSExn(stack.pop(), stack, f, pc, scope);
+
+ /* FIXME
+ case MAKE_GRAMMAR: {
+ final Grammar r = (Grammar)arg;
+ final JSScope final_scope = scope;
+ Grammar r2 = new Grammar() {
+ public int match(String s, int start, Hash v, JSScope scope) throws JSExn {
+ return r.match(s, start, v, final_scope);
+ }
+ public int matchAndWrite(String s, int start, Hash v, JSScope scope, String key) throws JSExn {
+ return r.matchAndWrite(s, start, v, final_scope, key);
+ }
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ Hash v = new Hash();
+ r.matchAndWrite((String)a0, 0, v, final_scope, "foo");
+ return v.get("foo");
+ }
+ };
+ Object obj = stack.pop();
+ if (obj != null && obj instanceof Grammar) r2 = new Grammar.Alternative((Grammar)obj, r2);
+ stack.push(r2);
+ break;
+ }
+ */
+ case ADD_TRAP: case DEL_TRAP: {
+ Object val = stack.pop();
+ Object key = stack.pop();
+ Object obj = stack.peek();
+ // A trap addition/removal
+ JS js = obj instanceof JSScope ? ((JSScope)obj).top() : (JS) obj;
+ if(op == ADD_TRAP) js.addTrap(key, (JSFunction)val);
+ else js.delTrap(key, (JSFunction)val);
+ break;
+ }
+
+ case ASSIGN_SUB: case ASSIGN_ADD: {
+ Object val = stack.pop();
+ Object key = stack.pop();
+ Object obj = stack.peek();
+ // The following setup is VERY important. The generated bytecode depends on the stack
+ // being setup like this (top to bottom) KEY, OBJ, VAL, KEY, OBJ
+ stack.push(key);
+ stack.push(val);
+ stack.push(obj);
+ stack.push(key);
+ break;
+ }
+
+ case ADD: {
+ int count = ((Number)arg).intValue();
+ if(count < 2) throw new Error("this should never happen");
+ if(count == 2) {
+ // common case
+ Object right = stack.pop();
+ Object left = stack.pop();
+ if(left instanceof String || right instanceof String)
+ stack.push(JS.toString(left).concat(JS.toString(right)));
+ else stack.push(JS.N(JS.toDouble(left) + JS.toDouble(right)));
+ } else {
+ Object[] args = new Object[count];
+ while(--count >= 0) args[count] = stack.pop();
+ if(args[0] instanceof String) {
+ StringBuffer sb = new StringBuffer(64);
+ for(int i=0;i> JS.toLong(right))); break;
+ case URSH: stack.push(JS.N(JS.toLong(left) >>> JS.toLong(right))); break;
+
+ case LT: case LE: case GT: case GE: {
+ if (left == null) left = JS.N(0);
+ if (right == null) right = JS.N(0);
+ int result = 0;
+ if (left instanceof String || right instanceof String) {
+ result = left.toString().compareTo(right.toString());
+ } else {
+ result = (int)java.lang.Math.ceil(JS.toDouble(left) - JS.toDouble(right));
+ }
+ stack.push(JS.B((op == LT && result < 0) || (op == LE && result <= 0) ||
+ (op == GT && result > 0) || (op == GE && result >= 0)));
+ break;
+ }
+
+ case EQ:
+ case NE: {
+ Object l = left;
+ Object r = right;
+ boolean ret;
+ if (l == null) { Object tmp = r; r = l; l = tmp; }
+ if (l == null && r == null) ret = true;
+ else if (r == null) ret = false; // l != null, so its false
+ else if (l instanceof Boolean) ret = JS.B(JS.toBoolean(r)).equals(l);
+ else if (l instanceof Number) ret = JS.toNumber(r).doubleValue() == JS.toNumber(l).doubleValue();
+ else if (l instanceof String) ret = r != null && l.equals(r.toString());
+ else ret = l.equals(r);
+ stack.push(JS.B(op == EQ ? ret : !ret)); break;
+ }
+
+ default: throw new Error("unknown opcode " + op);
+ } }
+ }
+
+ } catch(JSExn e) {
+ while(stack.size() > 0) {
+ Object o = stack.pop();
+ if (o instanceof CatchMarker || o instanceof TryMarker) {
+ boolean inCatch = o instanceof CatchMarker;
+ if(inCatch) {
+ o = stack.pop();
+ if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going
+ }
+ if(!inCatch && ((TryMarker)o).catchLoc >= 0) {
+ // run the catch block, this will implicitly run the finally block, if it exists
+ stack.push(o);
+ stack.push(catchMarker);
+ stack.push(e.getObject());
+ f = ((TryMarker)o).f;
+ scope = ((TryMarker)o).scope;
+ pc = ((TryMarker)o).catchLoc - 1;
+ continue OUTER;
+ } else {
+ stack.push(new FinallyData(e));
+ f = ((TryMarker)o).f;
+ scope = ((TryMarker)o).scope;
+ pc = ((TryMarker)o).finallyLoc - 1;
+ continue OUTER;
+ }
+ }
+ }
+ throw e;
+ } // end try/catch
+ } // end for
+ }
+
+
+
+ // Markers //////////////////////////////////////////////////////////////////////
+
+ public static class CallMarker {
+ int pc;
+ JSScope scope;
+ JSFunction f;
+ public CallMarker(Interpreter cx) { pc = cx.pc + 1; scope = cx.scope; f = cx.f; }
+ }
+
+ public static class CatchMarker { }
+ private static CatchMarker catchMarker = new CatchMarker();
+
+ public static class LoopMarker {
+ public int location;
+ public String label;
+ public JSScope scope;
+ public LoopMarker(int location, String label, JSScope scope) {
+ this.location = location;
+ this.label = label;
+ this.scope = scope;
+ }
+ }
+ public static class TryMarker {
+ public int catchLoc;
+ public int finallyLoc;
+ public JSScope scope;
+ public JSFunction f;
+ public TryMarker(int catchLoc, int finallyLoc, Interpreter cx) {
+ this.catchLoc = catchLoc;
+ this.finallyLoc = finallyLoc;
+ this.scope = cx.scope;
+ this.f = cx.f;
+ }
+ }
+ public static class FinallyData {
+ public int op;
+ public Object arg;
+ public JSExn exn;
+ public FinallyData(int op) { this(op,null); }
+ public FinallyData(int op, Object arg) { this.op = op; this.arg = arg; }
+ public FinallyData(JSExn exn) { this.exn = exn; } // Just throw this exn
+ }
+
+
+ // Operations on Primitives //////////////////////////////////////////////////////////////////////
+
+ static Object callMethodOnPrimitive(Object o, Object method, Object arg0, Object arg1, Object arg2, Object[] rest, int alength) throws JSExn {
+ if (method == null || !(method instanceof String) || "".equals(method))
+ throw new JSExn("attempt to call a non-existant method on a primitive");
+
+ if (o instanceof Number) {
+ //#switch(method)
+ case "toFixed": throw new JSExn("toFixed() not implemented");
+ case "toExponential": throw new JSExn("toExponential() not implemented");
+ case "toPrecision": throw new JSExn("toPrecision() not implemented");
+ case "toString": {
+ int radix = alength >= 1 ? JS.toInt(arg0) : 10;
+ return Long.toString(((Number)o).longValue(),radix);
+ }
+ //#end
+ } else if (o instanceof Boolean) {
+ // No methods for Booleans
+ throw new JSExn("attempt to call a method on a Boolean");
+ }
+
+ String s = JS.toString(o);
+ int slength = s.length();
+ //#switch(method)
+ case "substring": {
+ int a = alength >= 1 ? JS.toInt(arg0) : 0;
+ int b = alength >= 2 ? JS.toInt(arg1) : slength;
+ if (a > slength) a = slength;
+ if (b > slength) b = slength;
+ if (a < 0) a = 0;
+ if (b < 0) b = 0;
+ if (a > b) { int tmp = a; a = b; b = tmp; }
+ return s.substring(a,b);
+ }
+ case "substr": {
+ int start = alength >= 1 ? JS.toInt(arg0) : 0;
+ int len = alength >= 2 ? JS.toInt(arg1) : Integer.MAX_VALUE;
+ if (start < 0) start = slength + start;
+ if (start < 0) start = 0;
+ if (len < 0) len = 0;
+ if (len > slength - start) len = slength - start;
+ if (len <= 0) return "";
+ return s.substring(start,start+len);
+ }
+ case "charAt": {
+ int p = alength >= 1 ? JS.toInt(arg0) : 0;
+ if (p < 0 || p >= slength) return "";
+ return s.substring(p,p+1);
+ }
+ case "charCodeAt": {
+ int p = alength >= 1 ? JS.toInt(arg0) : 0;
+ if (p < 0 || p >= slength) return JS.N(Double.NaN);
+ return JS.N(s.charAt(p));
+ }
+ case "concat": {
+ StringBuffer sb = new StringBuffer(slength*2).append(s);
+ for(int i=0;i= 1 ? arg0.toString() : "null";
+ int start = alength >= 2 ? JS.toInt(arg1) : 0;
+ // Java's indexOf handles an out of bounds start index, it'll return -1
+ return JS.N(s.indexOf(search,start));
+ }
+ case "lastIndexOf": {
+ String search = alength >= 1 ? arg0.toString() : "null";
+ int start = alength >= 2 ? JS.toInt(arg1) : 0;
+ // Java's indexOf handles an out of bounds start index, it'll return -1
+ return JS.N(s.lastIndexOf(search,start));
+ }
+ case "match": return JSRegexp.stringMatch(s,arg0);
+ case "replace": return JSRegexp.stringReplace(s,arg0,arg1);
+ case "search": return JSRegexp.stringSearch(s,arg0);
+ case "split": return JSRegexp.stringSplit(s,arg0,arg1,alength);
+ case "toLowerCase": return s.toLowerCase();
+ case "toUpperCase": return s.toUpperCase();
+ case "toString": return s;
+ case "slice": {
+ int a = alength >= 1 ? JS.toInt(arg0) : 0;
+ int b = alength >= 2 ? JS.toInt(arg1) : slength;
+ if (a < 0) a = slength + a;
+ if (b < 0) b = slength + b;
+ if (a < 0) a = 0;
+ if (b < 0) b = 0;
+ if (a > slength) a = slength;
+ if (b > slength) b = slength;
+ if (a > b) return "";
+ return s.substring(a,b);
+ }
+ //#end
+ throw new JSExn("Attempted to call non-existent method: " + method);
+ }
+
+ static Object getFromPrimitive(Object o, Object key) throws JSExn {
+ boolean returnJS = false;
+ if (o instanceof Boolean) {
+ throw new JSExn("Booleans do not have properties");
+ } else if (o instanceof Number) {
+ if (key.equals("toPrecision") || key.equals("toExponential") || key.equals("toFixed"))
+ returnJS = true;
+ }
+ if (!returnJS) {
+ // the string stuff applies to everything
+ String s = o.toString();
+
+ // this is sort of ugly, but this list should never change
+ // These should provide a complete (enough) implementation of the ECMA-262 String object
+
+ //#switch(key)
+ case "length": return JS.N(s.length());
+ case "substring": returnJS = true; break;
+ case "charAt": returnJS = true; break;
+ case "charCodeAt": returnJS = true; break;
+ case "concat": returnJS = true; break;
+ case "indexOf": returnJS = true; break;
+ case "lastIndexOf": returnJS = true; break;
+ case "match": returnJS = true; break;
+ case "replace": returnJS = true; break;
+ case "seatch": returnJS = true; break;
+ case "slice": returnJS = true; break;
+ case "split": returnJS = true; break;
+ case "toLowerCase": returnJS = true; break;
+ case "toUpperCase": returnJS = true; break;
+ case "toString": returnJS = true; break;
+ case "substr": returnJS = true; break;
+ //#end
+ }
+ if (returnJS) {
+ final Object target = o;
+ final String method = key.toString();
+ return new JS() {
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ if (nargs > 2) throw new JSExn("cannot call that method with that many arguments");
+ return callMethodOnPrimitive(target, method, a0, a1, a2, rest, nargs);
+ }
+ };
+ }
+ return null;
+ }
+
+ private static class Stub extends JS {
+ private Object method;
+ JS obj;
+ public Stub(JS obj, Object method) { this.obj = obj; this.method = method; }
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ return ((JS)obj).callMethod(method, a0, a1, a2, rest, nargs);
+ }
+ }
+}
diff --git a/upstream/org.ibex.core/src/org/ibex/js/JS.java b/upstream/org.ibex.core/src/org/ibex/js/JS.java
new file mode 100644
index 0000000..7a78623
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/js/JS.java
@@ -0,0 +1,236 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+
+/** The minimum set of functionality required for objects which are manipulated by JavaScript */
+public class JS extends org.ibex.util.BalancedTree {
+
+ public static boolean checkAssertions = false;
+
+ public static final Object METHOD = new Object();
+ public final JS unclone() { return _unclone(); }
+ public Enumeration keys() throws JSExn { return entries == null ? emptyEnumeration : entries.keys(); }
+ public Object get(Object key) throws JSExn { return entries == null ? null : entries.get(key, null); }
+ public void put(Object key, Object val) throws JSExn { (entries==null?entries=new Hash():entries).put(key,null,val); }
+ public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ throw new JSExn("attempted to call the null value (method "+method+")");
+ }
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ throw new JSExn("you cannot call this object (class=" + this.getClass().getName() +")");
+ }
+
+ JS _unclone() { return this; }
+ public static class Cloneable extends JS {
+ public Object jsclone() throws JSExn {
+ return new Clone(this);
+ }
+ }
+
+ public static class Clone extends JS.Cloneable {
+ protected JS.Cloneable clonee = null;
+ JS _unclone() { return clonee.unclone(); }
+ public JS.Cloneable getClonee() { return clonee; }
+ public Clone(JS.Cloneable clonee) { this.clonee = clonee; }
+ public boolean equals(Object o) {
+ if (!(o instanceof JS)) return false;
+ return unclone() == ((JS)o).unclone();
+ }
+ public Enumeration keys() throws JSExn { return clonee.keys(); }
+ public Object get(Object key) throws JSExn { return clonee.get(key); }
+ public void put(Object key, Object val) throws JSExn { clonee.put(key, val); }
+ public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ return clonee.callMethod(method, a0, a1, a2, rest, nargs);
+ }
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ return clonee.call(a0, a1, a2, rest, nargs);
+ }
+ }
+
+ // Static Interpreter Control Methods ///////////////////////////////////////////////////////////////
+
+ /** log a message with the current JavaScript sourceName/line */
+ public static void log(Object message) { info(message); }
+ public static void debug(Object message) { Log.debug(Interpreter.getSourceName() + ":" + Interpreter.getLine(), message); }
+ public static void info(Object message) { Log.info(Interpreter.getSourceName() + ":" + Interpreter.getLine(), message); }
+ public static void warn(Object message) { Log.warn(Interpreter.getSourceName() + ":" + Interpreter.getLine(), message); }
+ public static void error(Object message) { Log.error(Interpreter.getSourceName() + ":" + Interpreter.getLine(), message); }
+
+ public static class NotPauseableException extends Exception { NotPauseableException() { } }
+
+ /** returns a callback which will restart the context; expects a value to be pushed onto the stack when unpaused */
+ public static UnpauseCallback pause() throws NotPauseableException {
+ Interpreter i = Interpreter.current();
+ if (i.pausecount == -1) throw new NotPauseableException();
+ i.pausecount++;
+ return new JS.UnpauseCallback(i);
+ }
+
+ public static class UnpauseCallback implements Task {
+ Interpreter i;
+ UnpauseCallback(Interpreter i) { this.i = i; }
+ public void perform() throws JSExn { unpause(null); }
+ public void unpause(Object o) throws JSExn {
+ // FIXME: if o instanceof JSExn, throw it into the JSworld
+ i.stack.push(o);
+ i.resume();
+ }
+ }
+
+
+
+ // Static Helper Methods ///////////////////////////////////////////////////////////////////////////////////
+
+ /** coerce an object to a Boolean */
+ public static boolean toBoolean(Object o) {
+ if (o == null) return false;
+ if (o instanceof Boolean) return ((Boolean)o).booleanValue();
+ if (o instanceof Long) return ((Long)o).longValue() != 0;
+ if (o instanceof Integer) return ((Integer)o).intValue() != 0;
+ if (o instanceof Number) {
+ double d = ((Number) o).doubleValue();
+ // NOTE: d == d is a test for NaN. It should be faster than Double.isNaN()
+ return d != 0.0 && d == d;
+ }
+ if (o instanceof String) return ((String)o).length() != 0;
+ return true;
+ }
+
+ /** coerce an object to a Long */
+ public static long toLong(Object o) { return toNumber(o).longValue(); }
+
+ /** coerce an object to an Int */
+ public static int toInt(Object o) { return toNumber(o).intValue(); }
+
+ /** coerce an object to a Double */
+ public static double toDouble(Object o) { return toNumber(o).doubleValue(); }
+
+ /** coerce an object to a Number */
+ public static Number toNumber(Object o) {
+ if (o == null) return ZERO;
+ if (o instanceof Number) return ((Number)o);
+
+ // NOTE: There are about 3 pages of rules in ecma262 about string to number conversions
+ // We aren't even close to following all those rules. We probably never will be.
+ if (o instanceof String) try { return N((String)o); } catch (NumberFormatException e) { return N(Double.NaN); }
+ if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? N(1) : ZERO;
+ throw new Error("toNumber() got object of type " + o.getClass().getName() + " which we don't know how to handle");
+ }
+
+ /** coerce an object to a String */
+ public static String toString(Object o) {
+ if(o == null) return "null";
+ if(o instanceof String) return (String) o;
+ if(o instanceof Integer || o instanceof Long || o instanceof Boolean) return o.toString();
+ if(o instanceof JSArray) return o.toString();
+ if(o instanceof JSDate) return o.toString();
+ if(o instanceof Double || o instanceof Float) {
+ double d = ((Number)o).doubleValue();
+ if((int)d == d) return Integer.toString((int)d);
+ return o.toString();
+ }
+ throw new RuntimeException("can't coerce "+o+" [" + o.getClass().getName() + "] to type String.");
+ }
+
+ // Instance Methods ////////////////////////////////////////////////////////////////////
+
+ public static final Integer ZERO = new Integer(0);
+
+ // this gets around a wierd fluke in the Java type checking rules for ?..:
+ public static final Object T = Boolean.TRUE;
+ public static final Object F = Boolean.FALSE;
+
+ public static final Boolean B(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; }
+ public static final Boolean B(int i) { return i==0 ? Boolean.FALSE : Boolean.TRUE; }
+ public static final Number N(String s) { return s.indexOf('.') == -1 ? N(Integer.parseInt(s)) : new Double(s); }
+ public static final Number N(double d) { return (int)d == d ? N((int)d) : new Double(d); }
+ public static final Number N(long l) { return N((int)l); }
+
+ private static final Integer[] smallIntCache = new Integer[65535 / 4];
+ private static final Integer[] largeIntCache = new Integer[65535 / 4];
+ public static final Number N(int i) {
+ Integer ret = null;
+ int idx = i + smallIntCache.length / 2;
+ if (idx < smallIntCache.length && idx > 0) {
+ ret = smallIntCache[idx];
+ if (ret != null) return ret;
+ }
+ else ret = largeIntCache[Math.abs(idx % largeIntCache.length)];
+ if (ret == null || ret.intValue() != i) {
+ ret = new Integer(i);
+ if (idx < smallIntCache.length && idx > 0) smallIntCache[idx] = ret;
+ else largeIntCache[Math.abs(idx % largeIntCache.length)] = ret;
+ }
+ return ret;
+ }
+
+ private static Enumeration emptyEnumeration = new Enumeration() {
+ public boolean hasMoreElements() { return false; }
+ public Object nextElement() { throw new NoSuchElementException(); }
+ };
+
+ private Hash entries = null;
+
+ public static JS fromReader(String sourceName, int firstLine, Reader sourceCode) throws IOException {
+ return JSFunction._fromReader(sourceName, firstLine, sourceCode);
+ }
+
+ // HACK: caller can't know if the argument is a JSFunction or not...
+ public static JS cloneWithNewParentScope(JS j, JSScope s) {
+ return ((JSFunction)j)._cloneWithNewParentScope(s);
+ }
+
+
+ // Trap support //////////////////////////////////////////////////////////////////////////////
+
+ /** override and return true to allow placing traps on this object.
+ * if isRead true, this is a read trap, otherwise write trap
+ **/
+ protected boolean isTrappable(Object name, boolean isRead) { return true; }
+
+ /** performs a put, triggering traps if present; traps are run in an unpauseable interpreter */
+ public void putAndTriggerTraps(Object key, Object value) throws JSExn {
+ Trap t = getTrap(key);
+ if (t != null) t.invoke(value);
+ else put(key, value);
+ }
+
+ /** performs a get, triggering traps if present; traps are run in an unpauseable interpreter */
+ public Object getAndTriggerTraps(Object key) throws JSExn {
+ Trap t = getTrap(key);
+ if (t != null) return t.invoke();
+ else return get(key);
+ }
+
+ /** retrieve a trap from the entries hash */
+ protected final Trap getTrap(Object key) {
+ return entries == null ? null : (Trap)entries.get(key, Trap.class);
+ }
+
+ /** retrieve a trap from the entries hash */
+ protected final void putTrap(Object key, Trap value) {
+ if (entries == null) entries = new Hash();
+ entries.put(key, Trap.class, value);
+ }
+
+ /** adds a trap, avoiding duplicates */
+ protected final void addTrap(Object name, JSFunction f) throws JSExn {
+ if (f.numFormalArgs > 1) throw new JSExn("traps must take either one argument (write) or no arguments (read)");
+ boolean isRead = f.numFormalArgs == 0;
+ if (!isTrappable(name, isRead)) throw new JSExn("not allowed "+(isRead?"read":"write")+" trap on property: "+name);
+ for(Trap t = getTrap(name); t != null; t = t.next) if (t.f == f) return;
+ putTrap(name, new Trap(this, name.toString(), f, (Trap)getTrap(name)));
+ }
+
+ /** deletes a trap, if present */
+ protected final void delTrap(Object name, JSFunction f) {
+ Trap t = (Trap)getTrap(name);
+ if (t == null) return;
+ if (t.f == f) { putTrap(t.name, t.next); return; }
+ for(; t.next != null; t = t.next) if (t.next.f == f) { t.next = t.next.next; return; }
+ }
+
+
+}
diff --git a/upstream/org.ibex.core/src/org/ibex/js/JSArray.java b/upstream/org.ibex.core/src/org/ibex/js/JSArray.java
new file mode 100644
index 0000000..7b90de7
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/js/JSArray.java
@@ -0,0 +1,262 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+import org.ibex.util.*;
+import java.util.*;
+
+/** A JavaScript JSArray */
+public class JSArray extends JS {
+ private static final Object NULL = new Object();
+
+ public JSArray() { }
+ public JSArray(int size) { setSize(size); }
+
+ private static int intVal(Object o) {
+ if (o instanceof Number) {
+ int intVal = ((Number)o).intValue();
+ if (intVal == ((Number)o).doubleValue()) return intVal;
+ return Integer.MIN_VALUE;
+ }
+ if (!(o instanceof String)) return Integer.MIN_VALUE;
+ String s = (String)o;
+ for(int i=0; i '9') return Integer.MIN_VALUE;
+ return Integer.parseInt(s);
+ }
+
+ public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ //#switch(method)
+ case "pop": {
+ int oldSize = size();
+ if(oldSize == 0) return null;
+ return removeElementAt(oldSize-1);
+ }
+ case "reverse": return reverse();
+ case "toString": return join(",");
+ case "shift":
+ if(length() == 0) return null;
+ return removeElementAt(0);
+ case "join":
+ return join(nargs == 0 ? "," : JS.toString(a0));
+ case "sort":
+ return sort(nargs < 1 ? null : a0);
+ case "slice":
+ int start = toInt(nargs < 1 ? null : a0);
+ int end = nargs < 2 ? length() : toInt(a1);
+ return slice(start, end);
+ case "push": {
+ int oldSize = size();
+ for(int i=0; i= size()) return null;
+ return elementAt(i);
+ }
+ //#switch(key)
+ case "pop": return METHOD;
+ case "reverse": return METHOD;
+ case "toString": return METHOD;
+ case "shift": return METHOD;
+ case "join": return METHOD;
+ case "sort": return METHOD;
+ case "slice": return METHOD;
+ case "push": return METHOD;
+ case "unshift": return METHOD;
+ case "splice": return METHOD;
+ case "length": return N(size());
+ //#end
+ return super.get(key);
+ }
+
+ public void put(Object key, Object val) throws JSExn {
+ if (key.equals("length")) setSize(toInt(val));
+ int i = intVal(key);
+ if (i == Integer.MIN_VALUE)
+ super.put(key, val);
+ else {
+ int oldSize = size();
+ if(i < oldSize) {
+ setElementAt(val,i);
+ } else {
+ if(i > oldSize) setSize(i);
+ insertElementAt(val,i);
+ }
+ }
+ }
+
+ public Enumeration keys() {
+ return new Enumeration() {
+ private int n = size();
+ public boolean hasMoreElements() { return n > 0; }
+ public Object nextElement() {
+ if(n == 0) throw new NoSuchElementException();
+ return new Integer(--n);
+ }
+ };
+ }
+
+ public final void setSize(int newSize) {
+ // FEATURE: This could be done a lot more efficiently in BalancedTree
+ int oldSize = size();
+ for(int i=oldSize;i=newSize;i--) removeElementAt(i);
+ }
+
+ public final int length() { return size(); }
+ public final Object elementAt(int i) {
+ if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
+ Object o = getNode(i);
+ return o == NULL ? null : o;
+ }
+ public final void addElement(Object o) {
+ insertNode(size(),o==null ? NULL : o);
+ }
+ public final void setElementAt(Object o, int i) {
+ if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
+ replaceNode(i,o==null ? NULL : o);
+ }
+ public final void insertElementAt(Object o, int i) {
+ if(i < 0 || i > size()) throw new ArrayIndexOutOfBoundsException(i);
+ insertNode(i,o==null ? NULL : o);
+ }
+ public final Object removeElementAt(int i) {
+ if(i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException(i);
+ Object o = deleteNode(i);
+ return o == NULL ? null : o;
+ }
+
+ public final int size() { return treeSize(); }
+ public String typeName() { return "array"; }
+
+ private Object join(String sep) {
+ int length = size();
+ if(length == 0) return "";
+ StringBuffer sb = new StringBuffer(64);
+ int i=0;
+ while(true) {
+ Object o = elementAt(i);
+ if(o != null) sb.append(JS.toString(o));
+ if(++i == length) break;
+ sb.append(sep);
+ }
+ return sb.toString();
+ }
+
+ // FEATURE: Implement this more efficiently
+ private Object reverse() {
+ int size = size();
+ if(size < 2) return this;
+ Vec vec = toVec();
+ clear();
+ for(int i=size-1,j=0;i>=0;i--,j++) insertElementAt(vec.elementAt(i),j);
+ return this;
+ }
+
+ private Object slice(int start, int end) {
+ int length = length();
+ if(start < 0) start = length+start;
+ if(end < 0) end = length+end;
+ if(start < 0) start = 0;
+ if(end < 0) end = 0;
+ if(start > length) start = length;
+ if(end > length) end = length;
+ JSArray a = new JSArray(end-start);
+ for(int i=0;i oldLength) start = oldLength;
+ if(deleteCount < 0) deleteCount = 0;
+ if(deleteCount > oldLength-start) deleteCount = oldLength-start;
+ int newLength = oldLength - deleteCount + newCount;
+ int lengthChange = newLength - oldLength;
+ JSArray ret = new JSArray(deleteCount);
+ for(int i=0;i 0) {
+ setSize(newLength);
+ for(int i=newLength-1;i>=start+newCount;i--)
+ setElementAt(elementAt(i-lengthChange),i);
+ } else if(lengthChange < 0) {
+ for(int i=start+newCount;i lo) {
+ mid = (hi + lo) / 2;
+ if (TimeFromYear(mid) > t) {
+ hi = mid - 1;
+ } else {
+ if (TimeFromYear(mid) <= t) {
+ int temp = mid + 1;
+ if (TimeFromYear(temp) > t) {
+ return mid;
+ }
+ lo = mid + 1;
+ }
+ }
+ }
+ return lo;
+ }
+
+ private static boolean InLeapYear(double t) {
+ return DaysInYear(YearFromTime(t)) == 366;
+ }
+
+ private static int DayWithinYear(double t) {
+ int year = YearFromTime(t);
+ return (int) (Day(t) - DayFromYear(year));
+ }
+ /*
+ * The following array contains the day of year for the first day of
+ * each month, where index 0 is January, and day 0 is January 1.
+ */
+
+ private static double DayFromMonth(int m, boolean leap) {
+ int day = m * 30;
+
+ if (m >= 7) { day += m / 2 - 1; }
+ else if (m >= 2) { day += (m - 1) / 2 - 1; }
+ else { day += m; }
+
+ if (leap && m >= 2) { ++day; }
+
+ return day;
+ }
+
+ private static int MonthFromTime(double t) {
+ int d, step;
+
+ d = DayWithinYear(t);
+
+ if (d < (step = 31))
+ return 0;
+
+ // Originally coded as step += (InLeapYear(t) ? 29 : 28);
+ // but some jits always returned 28!
+ if (InLeapYear(t))
+ step += 29;
+ else
+ step += 28;
+
+ if (d < step)
+ return 1;
+ if (d < (step += 31))
+ return 2;
+ if (d < (step += 30))
+ return 3;
+ if (d < (step += 31))
+ return 4;
+ if (d < (step += 30))
+ return 5;
+ if (d < (step += 31))
+ return 6;
+ if (d < (step += 31))
+ return 7;
+ if (d < (step += 30))
+ return 8;
+ if (d < (step += 31))
+ return 9;
+ if (d < (step += 30))
+ return 10;
+ return 11;
+ }
+
+ private static int DateFromTime(double t) {
+ int d, step, next;
+
+ d = DayWithinYear(t);
+ if (d <= (next = 30))
+ return d + 1;
+ step = next;
+
+ // Originally coded as next += (InLeapYear(t) ? 29 : 28);
+ // but some jits always returned 28!
+ if (InLeapYear(t))
+ next += 29;
+ else
+ next += 28;
+
+ if (d <= next)
+ return d - step;
+ step = next;
+ if (d <= (next += 31))
+ return d - step;
+ step = next;
+ if (d <= (next += 30))
+ return d - step;
+ step = next;
+ if (d <= (next += 31))
+ return d - step;
+ step = next;
+ if (d <= (next += 30))
+ return d - step;
+ step = next;
+ if (d <= (next += 31))
+ return d - step;
+ step = next;
+ if (d <= (next += 31))
+ return d - step;
+ step = next;
+ if (d <= (next += 30))
+ return d - step;
+ step = next;
+ if (d <= (next += 31))
+ return d - step;
+ step = next;
+ if (d <= (next += 30))
+ return d - step;
+ step = next;
+
+ return d - step;
+ }
+
+ private static int WeekDay(double t) {
+ double result;
+ result = Day(t) + 4;
+ result = result % 7;
+ if (result < 0)
+ result += 7;
+ return (int) result;
+ }
+
+ private static double Now() {
+ return (double) System.currentTimeMillis();
+ }
+
+ /* Should be possible to determine the need for this dynamically
+ * if we go with the workaround... I'm not using it now, because I
+ * can't think of any clean way to make toLocaleString() and the
+ * time zone (comment) in toString match the generated string
+ * values. Currently it's wrong-but-consistent in all but the
+ * most recent betas of the JRE - seems to work in 1.1.7.
+ */
+ private final static boolean TZO_WORKAROUND = false;
+ private static double DaylightSavingTA(double t) {
+ if (!TZO_WORKAROUND) {
+ java.util.Date date = new java.util.Date((long) t);
+ if (thisTimeZone.inDaylightTime(date))
+ return msPerHour;
+ else
+ return 0;
+ } else {
+ /* Use getOffset if inDaylightTime() is broken, because it
+ * seems to work acceptably. We don't switch over to it
+ * entirely, because it requires (expensive) exploded date arguments,
+ * and the api makes it impossible to handle dst
+ * changeovers cleanly.
+ */
+
+ // Hardcode the assumption that the changeover always
+ // happens at 2:00 AM:
+ t += LocalTZA + (HourFromTime(t) <= 2 ? msPerHour : 0);
+
+ int year = YearFromTime(t);
+ double offset = thisTimeZone.getOffset(year > 0 ? 1 : 0,
+ year,
+ MonthFromTime(t),
+ DateFromTime(t),
+ WeekDay(t),
+ (int)TimeWithinDay(t));
+
+ if ((offset - LocalTZA) != 0)
+ return msPerHour;
+ else
+ return 0;
+ // return offset - LocalTZA;
+ }
+ }
+
+ private static double LocalTime(double t) {
+ return t + LocalTZA + DaylightSavingTA(t);
+ }
+
+ public static double internalUTC(double t) {
+ return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
+ }
+
+ private static int HourFromTime(double t) {
+ double result;
+ result = java.lang.Math.floor(t / msPerHour) % HoursPerDay;
+ if (result < 0)
+ result += HoursPerDay;
+ return (int) result;
+ }
+
+ private static int MinFromTime(double t) {
+ double result;
+ result = java.lang.Math.floor(t / msPerMinute) % MinutesPerHour;
+ if (result < 0)
+ result += MinutesPerHour;
+ return (int) result;
+ }
+
+ private static int SecFromTime(double t) {
+ double result;
+ result = java.lang.Math.floor(t / msPerSecond) % SecondsPerMinute;
+ if (result < 0)
+ result += SecondsPerMinute;
+ return (int) result;
+ }
+
+ private static int msFromTime(double t) {
+ double result;
+ result = t % msPerSecond;
+ if (result < 0)
+ result += msPerSecond;
+ return (int) result;
+ }
+
+ private static double MakeTime(double hour, double min,
+ double sec, double ms)
+ {
+ return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec)
+ * msPerSecond + ms;
+ }
+
+ private static double MakeDay(double year, double month, double date) {
+ double result;
+ boolean leap;
+ double yearday;
+ double monthday;
+
+ year += java.lang.Math.floor(month / 12);
+
+ month = month % 12;
+ if (month < 0)
+ month += 12;
+
+ leap = (DaysInYear((int) year) == 366);
+
+ yearday = java.lang.Math.floor(TimeFromYear(year) / msPerDay);
+ monthday = DayFromMonth((int) month, leap);
+
+ result = yearday
+ + monthday
+ + date - 1;
+ return result;
+ }
+
+ private static double MakeDate(double day, double time) {
+ return day * msPerDay + time;
+ }
+
+ private static double TimeClip(double d) {
+ if (d != d ||
+ d == Double.POSITIVE_INFINITY ||
+ d == Double.NEGATIVE_INFINITY ||
+ java.lang.Math.abs(d) > HalfTimeDomain)
+ {
+ return Double.NaN;
+ }
+ if (d > 0.0)
+ return java.lang.Math.floor(d + 0.);
+ else
+ return java.lang.Math.ceil(d + 0.);
+ }
+
+ /* end of ECMA helper functions */
+
+ /* find UTC time from given date... no 1900 correction! */
+ public static double date_msecFromDate(double year, double mon,
+ double mday, double hour,
+ double min, double sec,
+ double msec)
+ {
+ double day;
+ double time;
+ double result;
+
+ day = MakeDay(year, mon, mday);
+ time = MakeTime(hour, min, sec, msec);
+ result = MakeDate(day, time);
+ return result;
+ }
+
+
+ private static final int MAXARGS = 7;
+ private static double jsStaticJSFunction_UTC(Object[] args) {
+ double array[] = new double[MAXARGS];
+ int loop;
+ double d;
+
+ for (loop = 0; loop < MAXARGS; loop++) {
+ if (loop < args.length) {
+ d = _toNumber(args[loop]);
+ if (d != d || Double.isInfinite(d)) {
+ return Double.NaN;
+ }
+ array[loop] = toDouble(args[loop]);
+ } else {
+ array[loop] = 0;
+ }
+ }
+
+ /* adjust 2-digit years into the 20th century */
+ if (array[0] >= 0 && array[0] <= 99)
+ array[0] += 1900;
+
+ /* if we got a 0 for 'date' (which is out of range)
+ * pretend it's a 1. (So Date.UTC(1972, 5) works) */
+ if (array[2] < 1)
+ array[2] = 1;
+
+ d = date_msecFromDate(array[0], array[1], array[2],
+ array[3], array[4], array[5], array[6]);
+ d = TimeClip(d);
+ return d;
+ // return N(d);
+ }
+
+ /*
+ * Use ported code from jsdate.c rather than the locale-specific
+ * date-parsing code from Java, to keep js and rhino consistent.
+ * Is this the right strategy?
+ */
+
+ /* for use by date_parse */
+
+ /* replace this with byte arrays? Cheaper? */
+ private static String wtb[] = {
+ "am", "pm",
+ "monday", "tuesday", "wednesday", "thursday", "friday",
+ "saturday", "sunday",
+ "january", "february", "march", "april", "may", "june",
+ "july", "august", "september", "october", "november", "december",
+ "gmt", "ut", "utc", "est", "edt", "cst", "cdt",
+ "mst", "mdt", "pst", "pdt"
+ /* time zone table needs to be expanded */
+ };
+
+ private static int ttb[] = {
+ -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
+ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 10000 + 0, 10000 + 0, 10000 + 0, /* UT/UTC */
+ 10000 + 5 * 60, 10000 + 4 * 60, /* EDT */
+ 10000 + 6 * 60, 10000 + 5 * 60,
+ 10000 + 7 * 60, 10000 + 6 * 60,
+ 10000 + 8 * 60, 10000 + 7 * 60
+ };
+
+ /* helper for date_parse */
+ private static boolean date_regionMatches(String s1, int s1off,
+ String s2, int s2off,
+ int count)
+ {
+ boolean result = false;
+ /* return true if matches, otherwise, false */
+ int s1len = s1.length();
+ int s2len = s2.length();
+
+ while (count > 0 && s1off < s1len && s2off < s2len) {
+ if (Character.toLowerCase(s1.charAt(s1off)) !=
+ Character.toLowerCase(s2.charAt(s2off)))
+ break;
+ s1off++;
+ s2off++;
+ count--;
+ }
+
+ if (count == 0) {
+ result = true;
+ }
+ return result;
+ }
+
+ private static double date_parseString(String s) {
+ double msec;
+
+ int year = -1;
+ int mon = -1;
+ int mday = -1;
+ int hour = -1;
+ int min = -1;
+ int sec = -1;
+ char c = 0;
+ char si = 0;
+ int i = 0;
+ int n = -1;
+ double tzoffset = -1;
+ char prevc = 0;
+ int limit = 0;
+ boolean seenplusminus = false;
+
+ if (s == null) // ??? Will s be null?
+ return Double.NaN;
+ limit = s.length();
+ while (i < limit) {
+ c = s.charAt(i);
+ i++;
+ if (c <= ' ' || c == ',' || c == '-') {
+ if (i < limit) {
+ si = s.charAt(i);
+ if (c == '-' && '0' <= si && si <= '9') {
+ prevc = c;
+ }
+ }
+ continue;
+ }
+ if (c == '(') { /* comments) */
+ int depth = 1;
+ while (i < limit) {
+ c = s.charAt(i);
+ i++;
+ if (c == '(')
+ depth++;
+ else if (c == ')')
+ if (--depth <= 0)
+ break;
+ }
+ continue;
+ }
+ if ('0' <= c && c <= '9') {
+ n = c - '0';
+ while (i < limit && '0' <= (c = s.charAt(i)) && c <= '9') {
+ n = n * 10 + c - '0';
+ i++;
+ }
+
+ /* allow TZA before the year, so
+ * 'Wed Nov 05 21:49:11 GMT-0800 1997'
+ * works */
+
+ /* uses of seenplusminus allow : in TZA, so Java
+ * no-timezone style of GMT+4:30 works
+ */
+ if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
+ /* make ':' case below change tzoffset */
+ seenplusminus = true;
+
+ /* offset */
+ if (n < 24)
+ n = n * 60; /* EG. "GMT-3" */
+ else
+ n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
+ if (prevc == '+') /* plus means east of GMT */
+ n = -n;
+ if (tzoffset != 0 && tzoffset != -1)
+ return Double.NaN;
+ tzoffset = n;
+ } else if (n >= 70 ||
+ (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
+ if (year >= 0)
+ return Double.NaN;
+ else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
+ year = n < 100 ? n + 1900 : n;
+ else
+ return Double.NaN;
+ } else if (c == ':') {
+ if (hour < 0)
+ hour = /*byte*/ n;
+ else if (min < 0)
+ min = /*byte*/ n;
+ else
+ return Double.NaN;
+ } else if (c == '/') {
+ if (mon < 0)
+ mon = /*byte*/ n-1;
+ else if (mday < 0)
+ mday = /*byte*/ n;
+ else
+ return Double.NaN;
+ } else if (i < limit && c != ',' && c > ' ' && c != '-') {
+ return Double.NaN;
+ } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
+ if (tzoffset < 0)
+ tzoffset -= n;
+ else
+ tzoffset += n;
+ } else if (hour >= 0 && min < 0) {
+ min = /*byte*/ n;
+ } else if (min >= 0 && sec < 0) {
+ sec = /*byte*/ n;
+ } else if (mday < 0) {
+ mday = /*byte*/ n;
+ } else {
+ return Double.NaN;
+ }
+ prevc = 0;
+ } else if (c == '/' || c == ':' || c == '+' || c == '-') {
+ prevc = c;
+ } else {
+ int st = i - 1;
+ int k;
+ while (i < limit) {
+ c = s.charAt(i);
+ if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
+ break;
+ i++;
+ }
+ if (i <= st + 1)
+ return Double.NaN;
+ for (k = wtb.length; --k >= 0;)
+ if (date_regionMatches(wtb[k], 0, s, st, i-st)) {
+ int action = ttb[k];
+ if (action != 0) {
+ if (action < 0) {
+ /*
+ * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
+ * 12:30, instead of blindly adding 12 if PM.
+ */
+ if (hour > 12 || hour < 0) {
+ return Double.NaN;
+ } else {
+ if (action == -1 && hour == 12) { // am
+ hour = 0;
+ } else if (action == -2 && hour != 12) {// pm
+ hour += 12;
+ }
+ }
+ } else if (action <= 13) { /* month! */
+ if (mon < 0) {
+ mon = /*byte*/ (action - 2);
+ } else {
+ return Double.NaN;
+ }
+ } else {
+ tzoffset = action - 10000;
+ }
+ }
+ break;
+ }
+ if (k < 0)
+ return Double.NaN;
+ prevc = 0;
+ }
+ }
+ if (year < 0 || mon < 0 || mday < 0)
+ return Double.NaN;
+ if (sec < 0)
+ sec = 0;
+ if (min < 0)
+ min = 0;
+ if (hour < 0)
+ hour = 0;
+ if (tzoffset == -1) { /* no time zone specified, have to use local */
+ double time;
+ time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
+ return internalUTC(time);
+ }
+
+ msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
+ msec += tzoffset * msPerMinute;
+ return msec;
+ }
+
+ private static double jsStaticJSFunction_parse(String s) {
+ return date_parseString(s);
+ }
+
+ private static final int FORMATSPEC_FULL = 0;
+ private static final int FORMATSPEC_DATE = 1;
+ private static final int FORMATSPEC_TIME = 2;
+
+ private static String date_format(double t, int format) {
+ if (t != t)
+ return NaN_date_str;
+
+ StringBuffer result = new StringBuffer(60);
+ double local = LocalTime(t);
+
+ /* offset from GMT in minutes. The offset includes daylight savings,
+ if it applies. */
+ int minutes = (int) java.lang.Math.floor((LocalTZA + DaylightSavingTA(t))
+ / msPerMinute);
+ /* map 510 minutes to 0830 hours */
+ int offset = (minutes / 60) * 100 + minutes % 60;
+
+ String dateStr = Integer.toString(DateFromTime(local));
+ String hourStr = Integer.toString(HourFromTime(local));
+ String minStr = Integer.toString(MinFromTime(local));
+ String secStr = Integer.toString(SecFromTime(local));
+ String offsetStr = Integer.toString(offset > 0 ? offset : -offset);
+ int year = YearFromTime(local);
+ String yearStr = Integer.toString(year > 0 ? year : -year);
+
+ /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */
+ /* Tue Oct 31 2000 */
+ /* 09:41:40 GMT-0800 (PST) */
+
+ if (format != FORMATSPEC_TIME) {
+ result.append(days[WeekDay(local)]);
+ result.append(' ');
+ result.append(months[MonthFromTime(local)]);
+ if (dateStr.length() == 1)
+ result.append(" 0");
+ else
+ result.append(' ');
+ result.append(dateStr);
+ result.append(' ');
+ }
+
+ if (format != FORMATSPEC_DATE) {
+ if (hourStr.length() == 1)
+ result.append('0');
+ result.append(hourStr);
+ if (minStr.length() == 1)
+ result.append(":0");
+ else
+ result.append(':');
+ result.append(minStr);
+ if (secStr.length() == 1)
+ result.append(":0");
+ else
+ result.append(':');
+ result.append(secStr);
+ if (offset > 0)
+ result.append(" GMT+");
+ else
+ result.append(" GMT-");
+ for (int i = offsetStr.length(); i < 4; i++)
+ result.append('0');
+ result.append(offsetStr);
+
+ if (timeZoneFormatter == null)
+ timeZoneFormatter = new java.text.SimpleDateFormat("zzz");
+
+ if (timeZoneFormatter != null) {
+ result.append(" (");
+ java.util.Date date = new java.util.Date((long) t);
+ result.append(timeZoneFormatter.format(date));
+ result.append(')');
+ }
+ if (format != FORMATSPEC_TIME)
+ result.append(' ');
+ }
+
+ if (format != FORMATSPEC_TIME) {
+ if (year < 0)
+ result.append('-');
+ for (int i = yearStr.length(); i < 4; i++)
+ result.append('0');
+ result.append(yearStr);
+ }
+
+ return result.toString();
+ }
+
+ private static double _toNumber(Object o) { return JS.toDouble(o); }
+ private static double _toNumber(Object[] o, int index) { return JS.toDouble(o[index]); }
+ private static double toDouble(double d) { return d; }
+
+ public JSDate(Object a0, Object a1, Object a2, Object[] rest, int nargs) {
+
+ JSDate obj = this;
+ switch (nargs) {
+ case 0: {
+ obj.date = Now();
+ return;
+ }
+ case 1: {
+ double date;
+ if (a0 instanceof JS)
+ a0 = ((JS) a0).toString();
+ if (!(a0 instanceof String)) {
+ // if it's not a string, use it as a millisecond date
+ date = _toNumber(a0);
+ } else {
+ // it's a string; parse it.
+ String str = (String) a0;
+ date = date_parseString(str);
+ }
+ obj.date = TimeClip(date);
+ return;
+ }
+ default: {
+ // multiple arguments; year, month, day etc.
+ double array[] = new double[MAXARGS];
+ array[0] = toDouble(a0);
+ array[1] = toDouble(a1);
+ if (nargs >= 2) array[2] = toDouble(a2);
+ for(int i=0; i= 0 && array[0] <= 99)
+ array[0] += 1900;
+
+ /* if we got a 0 for 'date' (which is out of range)
+ * pretend it's a 1 */
+ if (array[2] < 1)
+ array[2] = 1;
+
+ double day = MakeDay(array[0], array[1], array[2]);
+ double time = MakeTime(array[3], array[4], array[5], array[6]);
+ time = MakeDate(day, time);
+ time = internalUTC(time);
+ obj.date = TimeClip(time);
+
+ return;
+ }
+ }
+ }
+
+ /* constants for toString, toUTCString */
+ private static String NaN_date_str = "Invalid Date";
+
+ private static String[] days = {
+ "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
+ };
+
+ private static String[] months = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+
+ private static String toLocale_helper(double t,
+ java.text.DateFormat formatter)
+ {
+ if (t != t)
+ return NaN_date_str;
+
+ java.util.Date tempdate = new java.util.Date((long) t);
+ return formatter.format(tempdate);
+ }
+
+ private static String toLocaleString(double date) {
+ if (localeDateTimeFormatter == null)
+ localeDateTimeFormatter =
+ DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
+
+ return toLocale_helper(date, localeDateTimeFormatter);
+ }
+
+ private static String toLocaleTimeString(double date) {
+ if (localeTimeFormatter == null)
+ localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG);
+
+ return toLocale_helper(date, localeTimeFormatter);
+ }
+
+ private static String toLocaleDateString(double date) {
+ if (localeDateFormatter == null)
+ localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG);
+
+ return toLocale_helper(date, localeDateFormatter);
+ }
+
+ private static String toUTCString(double date) {
+ StringBuffer result = new StringBuffer(60);
+
+ String dateStr = Integer.toString(DateFromTime(date));
+ String hourStr = Integer.toString(HourFromTime(date));
+ String minStr = Integer.toString(MinFromTime(date));
+ String secStr = Integer.toString(SecFromTime(date));
+ int year = YearFromTime(date);
+ String yearStr = Integer.toString(year > 0 ? year : -year);
+
+ result.append(days[WeekDay(date)]);
+ result.append(", ");
+ if (dateStr.length() == 1)
+ result.append('0');
+ result.append(dateStr);
+ result.append(' ');
+ result.append(months[MonthFromTime(date)]);
+ if (year < 0)
+ result.append(" -");
+ else
+ result.append(' ');
+ int i;
+ for (i = yearStr.length(); i < 4; i++)
+ result.append('0');
+ result.append(yearStr);
+
+ if (hourStr.length() == 1)
+ result.append(" 0");
+ else
+ result.append(' ');
+ result.append(hourStr);
+ if (minStr.length() == 1)
+ result.append(":0");
+ else
+ result.append(':');
+ result.append(minStr);
+ if (secStr.length() == 1)
+ result.append(":0");
+ else
+ result.append(':');
+ result.append(secStr);
+
+ result.append(" GMT");
+ return result.toString();
+ }
+
+ private static double getYear(double date) {
+ int result = YearFromTime(LocalTime(date));
+ result -= 1900;
+ return result;
+ }
+
+ private static double getTimezoneOffset(double date) {
+ return (date - LocalTime(date)) / msPerMinute;
+ }
+
+ public double setTime(double time) {
+ this.date = TimeClip(time);
+ return this.date;
+ }
+
+ private double makeTime(Object[] args, int maxargs, boolean local) {
+ int i;
+ double conv[] = new double[4];
+ double hour, min, sec, msec;
+ double lorutime; /* Local or UTC version of date */
+
+ double time;
+ double result;
+
+ double date = this.date;
+
+ /* just return NaN if the date is already NaN */
+ if (date != date)
+ return date;
+
+ /* Satisfy the ECMA rule that if a function is called with
+ * fewer arguments than the specified formal arguments, the
+ * remaining arguments are set to undefined. Seems like all
+ * the Date.setWhatever functions in ECMA are only varargs
+ * beyond the first argument; this should be set to undefined
+ * if it's not given. This means that "d = new Date();
+ * d.setMilliseconds()" returns NaN. Blech.
+ */
+ if (args.length == 0)
+ args = new Object[] { null };
+
+ for (i = 0; i < args.length && i < maxargs; i++) {
+ conv[i] = _toNumber(args[i]);
+
+ // limit checks that happen in MakeTime in ECMA.
+ if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
+ this.date = Double.NaN;
+ return this.date;
+ }
+ conv[i] = toDouble(conv[i]);
+ }
+
+ if (local)
+ lorutime = LocalTime(date);
+ else
+ lorutime = date;
+
+ i = 0;
+ int stop = args.length;
+
+ if (maxargs >= 4 && i < stop)
+ hour = conv[i++];
+ else
+ hour = HourFromTime(lorutime);
+
+ if (maxargs >= 3 && i < stop)
+ min = conv[i++];
+ else
+ min = MinFromTime(lorutime);
+
+ if (maxargs >= 2 && i < stop)
+ sec = conv[i++];
+ else
+ sec = SecFromTime(lorutime);
+
+ if (maxargs >= 1 && i < stop)
+ msec = conv[i++];
+ else
+ msec = msFromTime(lorutime);
+
+ time = MakeTime(hour, min, sec, msec);
+ result = MakeDate(Day(lorutime), time);
+
+ if (local)
+ result = internalUTC(result);
+ date = TimeClip(result);
+
+ this.date = date;
+ return date;
+ }
+
+ private double setHours(Object[] args) {
+ return makeTime(args, 4, true);
+ }
+
+ private double setUTCHours(Object[] args) {
+ return makeTime(args, 4, false);
+ }
+
+ private double makeDate(Object[] args, int maxargs, boolean local) {
+ int i;
+ double conv[] = new double[3];
+ double year, month, day;
+ double lorutime; /* local or UTC version of date */
+ double result;
+
+ double date = this.date;
+
+ /* See arg padding comment in makeTime.*/
+ if (args.length == 0)
+ args = new Object[] { null };
+
+ for (i = 0; i < args.length && i < maxargs; i++) {
+ conv[i] = _toNumber(args[i]);
+
+ // limit checks that happen in MakeDate in ECMA.
+ if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
+ this.date = Double.NaN;
+ return this.date;
+ }
+ conv[i] = toDouble(conv[i]);
+ }
+
+ /* return NaN if date is NaN and we're not setting the year,
+ * If we are, use 0 as the time. */
+ if (date != date) {
+ if (args.length < 3) {
+ return Double.NaN;
+ } else {
+ lorutime = 0;
+ }
+ } else {
+ if (local)
+ lorutime = LocalTime(date);
+ else
+ lorutime = date;
+ }
+
+ i = 0;
+ int stop = args.length;
+
+ if (maxargs >= 3 && i < stop)
+ year = conv[i++];
+ else
+ year = YearFromTime(lorutime);
+
+ if (maxargs >= 2 && i < stop)
+ month = conv[i++];
+ else
+ month = MonthFromTime(lorutime);
+
+ if (maxargs >= 1 && i < stop)
+ day = conv[i++];
+ else
+ day = DateFromTime(lorutime);
+
+ day = MakeDay(year, month, day); /* day within year */
+ result = MakeDate(day, TimeWithinDay(lorutime));
+
+ if (local)
+ result = internalUTC(result);
+
+ date = TimeClip(result);
+
+ this.date = date;
+ return date;
+ }
+
+ private double setYear(double year) {
+ double day, result;
+ if (year != year || Double.isInfinite(year)) {
+ this.date = Double.NaN;
+ return this.date;
+ }
+
+ if (this.date != this.date) {
+ this.date = 0;
+ } else {
+ this.date = LocalTime(this.date);
+ }
+
+ if (year >= 0 && year <= 99)
+ year += 1900;
+
+ day = MakeDay(year, MonthFromTime(this.date), DateFromTime(this.date));
+ result = MakeDate(day, TimeWithinDay(this.date));
+ result = internalUTC(result);
+
+ this.date = TimeClip(result);
+ return this.date;
+ }
+
+
+ // private static final int
+ // Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6
+// #/string_id_map#
+
+ /* cached values */
+ private static java.util.TimeZone thisTimeZone;
+ private static double LocalTZA;
+ private static java.text.DateFormat timeZoneFormatter;
+ private static java.text.DateFormat localeDateTimeFormatter;
+ private static java.text.DateFormat localeDateFormatter;
+ private static java.text.DateFormat localeTimeFormatter;
+
+ private double date;
+
+ public long getRawTime() { return (long)this.date; }
+}
+
+
diff --git a/upstream/org.ibex.core/src/org/ibex/js/JSExn.java b/upstream/org.ibex.core/src/org/ibex/js/JSExn.java
new file mode 100644
index 0000000..b39d6c8
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/js/JSExn.java
@@ -0,0 +1,55 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+import org.ibex.util.*;
+import java.io.*;
+
+/** An exception which can be thrown and caught by JavaScript code */
+public class JSExn extends Exception {
+ private Vec backtrace = new Vec();
+ private Object js = null;
+ public JSExn(Object js) {
+ this.js = js;
+ if (Interpreter.current() != null)
+ fill(Interpreter.current().stack, Interpreter.current().f, Interpreter.current().pc, Interpreter.current().scope);
+ }
+ public JSExn(Object js, Vec stack, JSFunction f, int pc, JSScope scope) { this.js = js; fill(stack, f, pc, scope); }
+ private void fill(Vec stack, JSFunction f, int pc, JSScope scope) {
+ addBacktrace(f.sourceName + ":" + f.line[pc]);
+ if (scope != null && scope instanceof Trap.TrapScope)
+ addBacktrace("trap on property \"" + ((Trap.TrapScope)scope).t.name + "\"");
+ for(int i=stack.size()-1; i>=0; i--) {
+ Object element = stack.elementAt(i);
+ if (element instanceof Interpreter.CallMarker) {
+ Interpreter.CallMarker cm = (Interpreter.CallMarker)element;
+ if (cm.f != null)
+ addBacktrace(cm.f.sourceName + ":" + cm.f.line[cm.pc-1]);
+ if (cm.scope != null && cm.scope instanceof Trap.TrapScope)
+ addBacktrace("trap on property \"" + ((Trap.TrapScope)cm.scope).t.name + "\"");
+ }
+ }
+ }
+ public void printStackTrace() { printStackTrace(System.err); }
+ public void printStackTrace(PrintWriter pw) {
+ for(int i=0; iunpauseable context. */
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ JSArray args = new JSArray();
+ if (nargs > 0) args.addElement(a0);
+ if (nargs > 1) args.addElement(a1);
+ if (nargs > 2) args.addElement(a2);
+ for(int i=3; i= s.length()) { lastIndex = 0; return null; }
+ REMatch match = re.getMatch(s,start);
+ if(global) lastIndex = match == null ? s.length() : match.getEndIndex();
+ return match == null ? null : matchToExecResult(match,re,s);
+ }
+ case "test": {
+ String s = (String)a0;
+ if (!global) return B(re.getMatch(s) != null);
+ int start = global ? lastIndex : 0;
+ if(start < 0 || start >= s.length()) { lastIndex = 0; return null; }
+ REMatch match = re.getMatch(s,start);
+ lastIndex = match != null ? s.length() : match.getEndIndex();
+ return B(match != null);
+ }
+ case "toString": return toString(a0);
+ case "stringMatch": return stringMatch(a0,a1);
+ case "stringSearch": return stringSearch(a0,a1);
+ //#end
+ break;
+ }
+ case 2: {
+ //#switch(method)
+ case "stringReplace": return stringReplace(a0, a1,a2);
+ //#end
+ break;
+ }
+ }
+ return super.callMethod(method, a0, a1, a2, rest, nargs);
+ }
+
+ public Object get(Object key) throws JSExn {
+ //#switch(key)
+ case "exec": return METHOD;
+ case "test": return METHOD;
+ case "toString": return METHOD;
+ case "lastIndex": return N(lastIndex);
+ //#end
+ return super.get(key);
+ }
+
+ public void put(Object key, Object value) throws JSExn {
+ if(key.equals("lastIndex")) lastIndex = JS.toNumber(value).intValue();
+ super.put(key,value);
+ }
+
+ private static Object matchToExecResult(REMatch match, RE re, String s) {
+ try {
+ JS ret = new JS();
+ ret.put("index", N(match.getStartIndex()));
+ ret.put("input",s);
+ int n = re.getNumSubs();
+ ret.put("length", N(n+1));
+ ret.put("0",match.toString());
+ for(int i=1;i<=n;i++) ret.put(Integer.toString(i),match.toString(i));
+ return ret;
+ } catch (JSExn e) {
+ throw new Error("this should never happen");
+ }
+ }
+
+ public String toString() {
+ try {
+ StringBuffer sb = new StringBuffer();
+ sb.append('/');
+ sb.append(get("source"));
+ sb.append('/');
+ if(global) sb.append('g');
+ if(Boolean.TRUE.equals(get("ignoreCase"))) sb.append('i');
+ if(Boolean.TRUE.equals(get("multiline"))) sb.append('m');
+ return sb.toString();
+ } catch (JSExn e) {
+ throw new Error("this should never happen");
+ }
+ }
+
+ public static Object stringMatch(Object o, Object arg0) throws JSExn {
+ String s = o.toString();
+ RE re;
+ JSRegexp regexp = null;
+ if(arg0 instanceof JSRegexp) {
+ regexp = (JSRegexp) arg0;
+ re = regexp.re;
+ } else {
+ re = newRE(arg0.toString(),0);
+ }
+
+ if(regexp == null) {
+ REMatch match = re.getMatch(s);
+ return matchToExecResult(match,re,s);
+ }
+ if(!regexp.global) return regexp.callMethod("exec", s, null, null, null, 1);
+
+ JSArray ret = new JSArray();
+ REMatch[] matches = re.getAllMatches(s);
+ for(int i=0;i 0 ? matches[matches.length-1].getEndIndex() : s.length();
+ return ret;
+ }
+
+ public static Object stringSearch(Object o, Object arg0) throws JSExn {
+ String s = o.toString();
+ RE re = arg0 instanceof JSRegexp ? ((JSRegexp)arg0).re : newRE(arg0.toString(),0);
+ REMatch match = re.getMatch(s);
+ return match == null ? N(-1) : N(match.getStartIndex());
+ }
+
+ public static Object stringReplace(Object o, Object arg0, Object arg1) throws JSExn {
+ String s = o.toString();
+ RE re;
+ JSFunction replaceFunc = null;
+ String replaceString = null;
+ JSRegexp regexp = null;
+ if(arg0 instanceof JSRegexp) {
+ regexp = (JSRegexp) arg0;
+ re = regexp.re;
+ } else {
+ re = newRE(arg0.toString(),0);
+ }
+ if(arg1 instanceof JSFunction)
+ replaceFunc = (JSFunction) arg1;
+ else
+ replaceString = JS.toString(arg1.toString());
+ REMatch[] matches;
+ if(regexp != null && regexp.global) {
+ matches = re.getAllMatches(s);
+ if(regexp != null) {
+ if(matches.length > 0)
+ regexp.lastIndex = matches[matches.length-1].getEndIndex();
+ else
+ regexp.lastIndex = s.length();
+ }
+ } else {
+ REMatch match = re.getMatch(s);
+ if(match != null)
+ matches = new REMatch[]{ match };
+ else
+ matches = new REMatch[0];
+ }
+
+ StringBuffer sb = new StringBuffer(s.length());
+ int pos = 0;
+ char[] sa = s.toCharArray();
+ for(int i=0;i= '0' && c2 <= '9') {
+ n = (c - '0') * 10 + (c2 - '0');
+ i++;
+ } else {
+ n = c - '0';
+ }
+ if(n > 0)
+ sb.append(match.toString(n));
+ break;
+ case '$':
+ sb.append('$'); break;
+ case '&':
+ sb.append(match.toString()); break;
+ case '`':
+ sb.append(source.substring(0,match.getStartIndex())); break;
+ case '\'':
+ sb.append(source.substring(match.getEndIndex())); break;
+ default:
+ sb.append('$');
+ sb.append(c);
+ }
+ }
+ if(i < s.length()) sb.append(s.charAt(i));
+ return sb.toString();
+ }
+
+
+ public static Object stringSplit(String s, Object arg0, Object arg1, int nargs) {
+ int limit = nargs < 2 ? Integer.MAX_VALUE : JS.toInt(arg1);
+ if(limit < 0) limit = Integer.MAX_VALUE;
+ if(limit == 0) return new JSArray();
+
+ RE re = null;
+ JSRegexp regexp = null;
+ String sep = null;
+ JSArray ret = new JSArray();
+ int p = 0;
+
+ if(arg0 instanceof JSRegexp) {
+ regexp = (JSRegexp) arg0;
+ re = regexp.re;
+ } else {
+ sep = arg0.toString();
+ }
+
+ // special case this for speed. additionally, the code below doesn't properly handle
+ // zero length strings
+ if(sep != null && sep.length()==0) {
+ int len = s.length();
+ for(int i=0;i 36)) return NaN;
+ while(start < length && Character.isWhitespace(s.charAt(start))) start++;
+ if((length >= start+1) && (s.charAt(start) == '+' || s.charAt(start) == '-')) {
+ sign = s.charAt(start) == '+' ? 1 : -1;
+ start++;
+ }
+ if(radix == 0 && length >= start+1 && s.charAt(start) == '0') {
+ start++;
+ if(length >= start+1 && (s.charAt(start) == 'x' || s.charAt(start) == 'X')) {
+ start++;
+ radix = 16;
+ } else {
+ radix = 8;
+ if(length == start || Character.digit(s.charAt(start+1),8)==-1) return JS.ZERO;
+ }
+ }
+ if(radix == 0) radix = 10;
+ if(length == start || Character.digit(s.charAt(start),radix) == -1) return NaN;
+ // try the fast way first
+ try {
+ String s2 = start == 0 ? s : s.substring(start);
+ return JS.N(sign*Integer.parseInt(s2,radix));
+ } catch(NumberFormatException e) { }
+ // fall through to a slower but emca-compliant method
+ for(int i=start;iregardless of pushbacks */
+ protected int mostRecentlyReadToken;
+
+ /** if the token just parsed was a NUMBER, this is the numeric value */
+ protected Number number = null;
+
+ /** if the token just parsed was a NAME or STRING, this is the string value */
+ protected String string = null;
+
+ /** the line number of the most recently lexed token */
+ protected int line = 0;
+
+ /** the line number of the most recently parsed token */
+ protected int parserLine = 0;
+
+ /** the column number of the current token */
+ protected int col = 0;
+
+ /** the name of the source code file being lexed */
+ protected String sourceName;
+
+ private SmartReader in;
+ public Lexer(Reader r, String sourceName, int line) throws IOException {
+ this.sourceName = sourceName;
+ this.line = line;
+ this.parserLine = line;
+ in = new SmartReader(r);
+ }
+
+
+ // Predicates ///////////////////////////////////////////////////////////////////////
+
+ private static boolean isAlpha(int c) { return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); }
+ private static boolean isDigit(int c) { return (c >= '0' && c <= '9'); }
+ private static int xDigitToInt(int c) {
+ if ('0' <= c && c <= '9') return c - '0';
+ else if ('a' <= c && c <= 'f') return c - ('a' - 10);
+ else if ('A' <= c && c <= 'F') return c - ('A' - 10);
+ else return -1;
+ }
+
+
+ // Token Subtype Handlers /////////////////////////////////////////////////////////
+
+ private int getKeyword(String name) throws IOException {
+ //#switch(name)
+ case "if": return IF;
+ case "lt": return LT;
+ case "gt": return GT;
+ case "in": return IN;
+ case "do": return DO;
+ case "and": return AND;
+ case "or": return OR;
+ case "for": return FOR;
+ case "int": return RESERVED;
+ case "new": return RESERVED;
+ case "try": return TRY;
+ case "var": return VAR;
+ case "byte": return RESERVED;
+ case "case": return CASE;
+ case "char": return RESERVED;
+ case "else": return ELSE;
+ case "enum": return RESERVED;
+ case "goto": return RESERVED;
+ case "long": return RESERVED;
+ case "null": return NULL;
+ case "true": return TRUE;
+ case "with": return RESERVED;
+ case "void": return RESERVED;
+ case "class": return RESERVED;
+ case "break": return BREAK;
+ case "while": return WHILE;
+ case "false": return FALSE;
+ case "const": return RESERVED;
+ case "final": return RESERVED;
+ case "super": return RESERVED;
+ case "throw": return THROW;
+ case "catch": return CATCH;
+ case "class": return RESERVED;
+ case "delete": return RESERVED;
+ case "return": return RETURN;
+ case "throws": return RESERVED;
+ case "double": return RESERVED;
+ case "assert": return ASSERT;
+ case "public": return RESERVED;
+ case "switch": return SWITCH;
+ case "typeof": return TYPEOF;
+ case "package": return RESERVED;
+ case "default": return DEFAULT;
+ case "finally": return FINALLY;
+ case "boolean": return RESERVED;
+ case "private": return RESERVED;
+ case "extends": return RESERVED;
+ case "abstract": return RESERVED;
+ case "continue": return CONTINUE;
+ case "debugger": return RESERVED;
+ case "function": return FUNCTION;
+ case "volatile": return RESERVED;
+ case "interface": return RESERVED;
+ case "protected": return RESERVED;
+ case "transient": return RESERVED;
+ case "implements": return RESERVED;
+ case "instanceof": return RESERVED;
+ case "synchronized": return RESERVED;
+ //#end
+ return -1;
+ }
+
+ private int getIdentifier(int c) throws IOException {
+ in.startString();
+ while (Character.isJavaIdentifierPart((char)(c = in.read())));
+ in.unread();
+ String str = in.getString();
+ int result = getKeyword(str);
+ if (result == RESERVED) throw new LexerException("The reserved word \"" + str + "\" is not permitted in Ibex scripts");
+ if (result != -1) return result;
+ this.string = str.intern();
+ return NAME;
+ }
+
+ private int getNumber(int c) throws IOException {
+ int base = 10;
+ in.startString();
+ double dval = Double.NaN;
+ long longval = 0;
+ boolean isInteger = true;
+
+ // figure out what base we're using
+ if (c == '0') {
+ if (Character.toLowerCase((char)(c = in.read())) == 'x') { base = 16; in.startString(); }
+ else if (isDigit(c)) base = 8;
+ }
+
+ while (0 <= xDigitToInt(c) && !(base < 16 && isAlpha(c))) c = in.read();
+ if (base == 10 && (c == '.' || c == 'e' || c == 'E')) {
+ isInteger = false;
+ if (c == '.') do { c = in.read(); } while (isDigit(c));
+ if (c == 'e' || c == 'E') {
+ c = in.read();
+ if (c == '+' || c == '-') c = in.read();
+ if (!isDigit(c)) throw new LexerException("float listeral did not have an exponent value");
+ do { c = in.read(); } while (isDigit(c));
+ }
+ }
+ in.unread();
+
+ String numString = in.getString();
+ if (base == 10 && !isInteger) {
+ try { dval = (Double.valueOf(numString)).doubleValue(); }
+ catch (NumberFormatException ex) { throw new LexerException("invalid numeric literal: \"" + numString + "\""); }
+ } else {
+ if (isInteger) {
+ longval = Long.parseLong(numString, base);
+ dval = (double)longval;
+ } else {
+ dval = Double.parseDouble(numString);
+ longval = (long) dval;
+ if (longval == dval) isInteger = true;
+ }
+ }
+
+ if (!isInteger) this.number = JS.N(dval);
+ else this.number = JS.N(longval);
+ return NUMBER;
+ }
+
+ private int getString(int c) throws IOException {
+ StringBuffer stringBuf = null;
+ int quoteChar = c;
+ c = in.read();
+ in.startString(); // start after the first "
+ while(c != quoteChar) {
+ if (c == '\n' || c == -1) throw new LexerException("unterminated string literal");
+ if (c == '\\') {
+ if (stringBuf == null) {
+ in.unread(); // Don't include the backslash
+ stringBuf = new StringBuffer(in.getString());
+ in.read();
+ }
+ switch (c = in.read()) {
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'v': c = '\u000B'; break;
+ case '\\': c = '\\'; break;
+ case 'u': {
+ int v = 0;
+ for(int i=0; i<4; i++) {
+ int ci = in.read();
+ if (!((ci >= '0' && ci <= '9') || (ci >= 'a' && ci <= 'f') || (ci >= 'A' && ci <= 'F')))
+ throw new LexerException("illegal character '" + ((char)c) + "' in \\u unicode escape sequence");
+ v = (v << 8) | Integer.parseInt(ci + "", 16);
+ }
+ c = (char)v;
+ break;
+ }
+ default:
+ // just use the character that was escaped
+ break;
+ }
+ }
+ if (stringBuf != null) stringBuf.append((char) c);
+ c = in.read();
+ }
+ if (stringBuf != null) this.string = stringBuf.toString().intern();
+ else {
+ in.unread(); // miss the trailing "
+ this.string = in.getString().intern();
+ in.read();
+ }
+ return STRING;
+ }
+
+ private int _getToken() throws IOException {
+ int c;
+ do { c = in.read(); } while (c == '\u0020' || c == '\u0009' || c == '\u000C' || c == '\u000B' || c == '\n' );
+ if (c == -1) return -1;
+ if (c == '\\' || Character.isJavaIdentifierStart((char)c)) return getIdentifier(c);
+ if (isDigit(c) || (c == '.' && isDigit(in.peek()))) return getNumber(c);
+ if (c == '"' || c == '\'') return getString(c);
+ switch (c) {
+ case ';': return SEMI;
+ case '[': return LB;
+ case ']': return RB;
+ case '{': return LC;
+ case '}': return RC;
+ case '(': return LP;
+ case ')': return RP;
+ case ',': return COMMA;
+ case '?': return HOOK;
+ case ':': return !in.match(':') ? COLON : in.match('=') ? GRAMMAR : le(":: is not a valid token");
+ case '.': return DOT;
+ case '|': return in.match('|') ? OR : (in.match('=') ? ASSIGN_BITOR : BITOR);
+ case '^': return in.match('=') ? ASSIGN_BITXOR : BITXOR;
+ case '&': return in.match('&') ? AND : in.match('=') ? ASSIGN_BITAND : BITAND;
+ case '=': return !in.match('=') ? ASSIGN : in.match('=') ? SHEQ : EQ;
+ case '!': return !in.match('=') ? BANG : in.match('=') ? SHNE : NE;
+ case '%': return in.match('=') ? ASSIGN_MOD : MOD;
+ case '~': return BITNOT;
+ case '+': return in.match('=') ? ASSIGN_ADD : in.match('+') ? (in.match('=') ? ADD_TRAP : INC) : ADD;
+ case '-': return in.match('=') ? ASSIGN_SUB: in.match('-') ? (in.match('=') ? DEL_TRAP : DEC) : SUB;
+ case '*': return in.match('=') ? ASSIGN_MUL : MUL;
+ case '<': return !in.match('<') ? (in.match('=') ? LE : LT) : in.match('=') ? ASSIGN_LSH : LSH;
+ case '>': return !in.match('>') ? (in.match('=') ? GE : GT) :
+ in.match('>') ? (in.match('=') ? ASSIGN_URSH : URSH) : (in.match('=') ? ASSIGN_RSH : RSH);
+ case '/':
+ if (in.match('=')) return ASSIGN_DIV;
+ if (in.match('/')) { while ((c = in.read()) != -1 && c != '\n'); in.unread(); return getToken(); }
+ if (!in.match('*')) return DIV;
+ while ((c = in.read()) != -1 && !(c == '*' && in.match('/'))) {
+ if (c == '\n' || c != '/' || !in.match('*')) continue;
+ if (in.match('/')) return getToken();
+ throw new LexerException("nested comments are not permitted");
+ }
+ if (c == -1) throw new LexerException("unterminated comment");
+ return getToken(); // `goto retry'
+ default: throw new LexerException("illegal character: \'" + ((char)c) + "\'");
+ }
+ }
+
+ private int le(String s) throws LexerException { if (true) throw new LexerException(s); return 0; }
+
+ // SmartReader ////////////////////////////////////////////////////////////////
+
+ /** a Reader that tracks line numbers and can push back tokens */
+ private class SmartReader {
+ PushbackReader reader = null;
+ int lastread = -1;
+
+ public SmartReader(Reader r) { reader = new PushbackReader(r); }
+ public void unread() throws IOException { unread((char)lastread); }
+ public void unread(char c) throws IOException {
+ reader.unread(c);
+ if(c == '\n') col = -1;
+ else col--;
+ if (accumulator != null) accumulator.setLength(accumulator.length() - 1);
+ }
+ public boolean match(char c) throws IOException { if (peek() == c) { reader.read(); return true; } else return false; }
+ public int peek() throws IOException {
+ int peeked = reader.read();
+ if (peeked != -1) reader.unread((char)peeked);
+ return peeked;
+ }
+ public int read() throws IOException {
+ lastread = reader.read();
+ if (accumulator != null) accumulator.append((char)lastread);
+ if (lastread != '\n' && lastread != '\r') col++;
+ if (lastread == '\n') {
+ // col is -1 if we just unread a newline, this is sort of ugly
+ if (col != -1) parserLine = ++line;
+ col = 0;
+ }
+ return lastread;
+ }
+
+ // FEATURE: could be much more efficient
+ StringBuffer accumulator = null;
+ public void startString() {
+ accumulator = new StringBuffer();
+ accumulator.append((char)lastread);
+ }
+ public String getString() throws IOException {
+ String ret = accumulator.toString().intern();
+ accumulator = null;
+ return ret;
+ }
+ }
+
+
+ // Token PushBack code ////////////////////////////////////////////////////////////
+
+ private int pushBackDepth = 0;
+ private int[] pushBackInts = new int[10];
+ private Object[] pushBackObjects = new Object[10];
+
+ /** push back a token */
+ public final void pushBackToken(int op, Object obj) {
+ if (pushBackDepth >= pushBackInts.length - 1) {
+ int[] newInts = new int[pushBackInts.length * 2];
+ System.arraycopy(pushBackInts, 0, newInts, 0, pushBackInts.length);
+ pushBackInts = newInts;
+ Object[] newObjects = new Object[pushBackObjects.length * 2];
+ System.arraycopy(pushBackObjects, 0, newObjects, 0, pushBackObjects.length);
+ pushBackObjects = newObjects;
+ }
+ pushBackInts[pushBackDepth] = op;
+ pushBackObjects[pushBackDepth] = obj;
+ pushBackDepth++;
+ }
+
+ /** push back the most recently read token */
+ public final void pushBackToken() { pushBackToken(op, number != null ? (Object)number : (Object)string); }
+
+ /** read a token but leave it in the stream */
+ public final int peekToken() throws IOException {
+ int ret = getToken();
+ pushBackToken();
+ return ret;
+ }
+
+ /** read a token */
+ public final int getToken() throws IOException {
+ number = null;
+ string = null;
+ if (pushBackDepth == 0) {
+ mostRecentlyReadToken = op;
+ return op = _getToken();
+ }
+ pushBackDepth--;
+ op = pushBackInts[pushBackDepth];
+ if (pushBackObjects[pushBackDepth] != null) {
+ number = pushBackObjects[pushBackDepth] instanceof Number ? (Number)pushBackObjects[pushBackDepth] : null;
+ string = pushBackObjects[pushBackDepth] instanceof String ? (String)pushBackObjects[pushBackDepth] : null;
+ }
+ return op;
+ }
+
+ class LexerException extends IOException {
+ public LexerException(String s) { super(sourceName + ":" + line + "," + col + ": " + s); }
+ }
+}
diff --git a/upstream/org.ibex.core/src/org/ibex/js/Parser.java b/upstream/org.ibex.core/src/org/ibex/js/Parser.java
new file mode 100644
index 0000000..51b23b8
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/js/Parser.java
@@ -0,0 +1,982 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+import org.ibex.util.*;
+import java.io.*;
+
+/**
+ * Parses a stream of lexed tokens into a tree of JSFunction's.
+ *
+ * There are three kinds of things we parse: blocks, statements, and
+ * expressions.
+ *
+ * - Expressions are a special type of statement that evaluates to a
+ * value (for example, "break" is not an expression, * but "3+2"
+ * is). Some tokens sequences start expressions (for * example,
+ * literal numbers) and others continue an expression which * has
+ * already been begun (for example, '+'). Finally, some *
+ * expressions are valid targets for an assignment operation; after
+ * * each of these expressions, continueExprAfterAssignable() is
+ * called * to check for an assignment operation.
+ *
+ * - A statement ends with a semicolon and does not return a value.
+ *
+ * - A block is a single statement or a sequence of statements
+ * surrounded by curly braces.
+ *
+ * Each parsing method saves the parserLine before doing its actual
+ * work and restores it afterwards. This ensures that parsing a
+ * subexpression does not modify the line number until a token
+ * *after* the subexpression has been consumed by the parent
+ * expression.
+ *
+ * Technically it would be a better design for this class to build an
+ * intermediate parse tree and use that to emit bytecode. Here's the
+ * tradeoff:
+ *
+ * Advantages of building a parse tree:
+ * - easier to apply optimizations
+ * - would let us handle more sophisticated languages than JavaScript
+ *
+ * Advantages of leaving out the parse tree
+ * - faster compilation
+ * - less load on the garbage collector
+ * - much simpler code, easier to understand
+ * - less error-prone
+ *
+ * Fortunately JS is such a simple language that we can get away with
+ * the half-assed approach and still produce a working, complete
+ * compiler.
+ *
+ * The bytecode language emitted doesn't really cause any appreciable
+ * semantic loss, and is itself a parseable language very similar to
+ * Forth or a postfix variant of LISP. This means that the bytecode
+ * can be transformed into a parse tree, which can be manipulated.
+ * So if we ever want to add an optimizer, it could easily be done by
+ * producing a parse tree from the bytecode, optimizing that tree,
+ * and then re-emitting the bytecode. The parse tree node class
+ * would also be much simpler since the bytecode language has so few
+ * operators.
+ *
+ * Actually, the above paragraph is slightly inaccurate -- there are
+ * places where we push a value and then perform an arbitrary number
+ * of operations using it before popping it; this doesn't parse well.
+ * But these cases are clearly marked and easy to change if we do
+ * need to move to a parse tree format.
+ */
+class Parser extends Lexer implements ByteCodes {
+
+
+ // Constructors //////////////////////////////////////////////////////
+
+ public Parser(Reader r, String sourceName, int line) throws IOException { super(r, sourceName, line); }
+
+ /** for debugging */
+ public static void main(String[] s) throws IOException {
+ JS block = JS.fromReader("stdin", 0, new InputStreamReader(System.in));
+ if (block == null) return;
+ System.out.println(block);
+ }
+
+
+ // Statics ////////////////////////////////////////////////////////////
+
+ static byte[] precedence = new byte[MAX_TOKEN + 1];
+ static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1];
+ // Use this as the precedence when we want anything up to the comma
+ private final static int NO_COMMA = 2;
+ static {
+ isRightAssociative[ASSIGN] =
+ isRightAssociative[ASSIGN_BITOR] =
+ isRightAssociative[ASSIGN_BITXOR] =
+ isRightAssociative[ASSIGN_BITAND] =
+ isRightAssociative[ASSIGN_LSH] =
+ isRightAssociative[ASSIGN_RSH] =
+ isRightAssociative[ASSIGN_URSH] =
+ isRightAssociative[ASSIGN_ADD] =
+ isRightAssociative[ASSIGN_SUB] =
+ isRightAssociative[ASSIGN_MUL] =
+ isRightAssociative[ASSIGN_DIV] =
+ isRightAssociative[ASSIGN_MOD] =
+ isRightAssociative[ADD_TRAP] =
+ isRightAssociative[DEL_TRAP] =
+ true;
+
+ precedence[COMMA] = 1;
+ // 2 is intentionally left unassigned. we use minPrecedence==2 for comma separated lists
+ precedence[ASSIGN] =
+ precedence[ASSIGN_BITOR] =
+ precedence[ASSIGN_BITXOR] =
+ precedence[ASSIGN_BITAND] =
+ precedence[ASSIGN_LSH] =
+ precedence[ASSIGN_RSH] =
+ precedence[ASSIGN_URSH] =
+ precedence[ASSIGN_ADD] =
+ precedence[ASSIGN_SUB] =
+ precedence[ASSIGN_MUL] =
+ precedence[ASSIGN_DIV] =
+ precedence[ADD_TRAP] =
+ precedence[DEL_TRAP] =
+ precedence[ASSIGN_MOD] = 3;
+ precedence[HOOK] = 4;
+ precedence[OR] = 5;
+ precedence[AND] = 6;
+ precedence[BITOR] = 7;
+ precedence[BITXOR] = 8;
+ precedence[BITAND] = 9;
+ precedence[EQ] = precedence[NE] = precedence[SHEQ] = precedence[SHNE] = 10;
+ precedence[LT] = precedence[LE] = precedence[GT] = precedence[GE] = 11;
+ precedence[LSH] = precedence[RSH] = precedence[URSH] = 12;
+ precedence[ADD] = precedence[SUB] = 12;
+ precedence[MUL] = precedence[DIV] = precedence[MOD] = 13;
+ precedence[BITNOT] = precedence[BANG] = precedence[TYPEOF] = 14;
+ precedence[DOT] = precedence[LB] = precedence[LP] = precedence[INC] = precedence[DEC] = 15;
+ }
+
+
+ // Parsing Logic /////////////////////////////////////////////////////////
+
+ /** gets a token and throws an exception if it is not code */
+ private void consume(int code) throws IOException {
+ if (getToken() != code) {
+ if(code == NAME) switch(op) {
+ case RETURN: case TYPEOF: case BREAK: case CONTINUE: case TRY: case THROW:
+ case ASSERT: case NULL: case TRUE: case FALSE: case IN: case IF: case ELSE:
+ case SWITCH: case CASE: case DEFAULT: case WHILE: case VAR: case WITH:
+ case CATCH: case FINALLY:
+ throw pe("Bad variable name; '" + codeToString[op].toLowerCase() + "' is a javascript keyword");
+ }
+ throw pe("expected " + codeToString[code] + ", got " + (op == -1 ? "EOF" : codeToString[op]));
+ }
+ }
+
+ /**
+ * Parse the largest possible expression containing no operators
+ * of precedence below minPrecedence and append the
+ * bytecodes for that expression to appendTo; the
+ * appended bytecodes MUST grow the stack by exactly one element.
+ */
+ private void startExpr(JSFunction appendTo, int minPrecedence) throws IOException {
+ int saveParserLine = parserLine;
+ _startExpr(appendTo, minPrecedence);
+ parserLine = saveParserLine;
+ }
+ private void _startExpr(JSFunction appendTo, int minPrecedence) throws IOException {
+ int tok = getToken();
+ JSFunction b = appendTo;
+
+ switch (tok) {
+ case -1: throw pe("expected expression");
+
+ // all of these simply push values onto the stack
+ case NUMBER: b.add(parserLine, LITERAL, number); break;
+ case STRING: b.add(parserLine, LITERAL, string); break;
+ case NULL: b.add(parserLine, LITERAL, null); break;
+ case TRUE: case FALSE: b.add(parserLine, LITERAL, JS.B(tok == TRUE)); break;
+
+ // (.foo) syntax
+ case DOT: {
+ consume(NAME);
+ b.add(parserLine, TOPSCOPE);
+ b.add(parserLine, LITERAL, "");
+ b.add(parserLine, GET);
+ b.add(parserLine, LITERAL, string);
+ b.add(parserLine, GET);
+ continueExpr(b, minPrecedence);
+ break;
+ }
+
+ case LB: {
+ b.add(parserLine, ARRAY, JS.ZERO); // push an array onto the stack
+ int size0 = b.size;
+ int i = 0;
+ if (peekToken() != RB)
+ while(true) { // iterate over the initialization values
+ b.add(parserLine, LITERAL, JS.N(i++)); // push the index in the array to place it into
+ if (peekToken() == COMMA || peekToken() == RB)
+ b.add(parserLine, LITERAL, null); // for stuff like [1,,2,]
+ else
+ startExpr(b, NO_COMMA); // push the value onto the stack
+ b.add(parserLine, PUT); // put it into the array
+ b.add(parserLine, POP); // discard the value remaining on the stack
+ if (peekToken() == RB) break;
+ consume(COMMA);
+ }
+ b.set(size0 - 1, JS.N(i)); // back at the ARRAY instruction, write the size of the array
+ consume(RB);
+ break;
+ }
+ case SUB: { // negative literal (like "3 * -1")
+ consume(NUMBER);
+ b.add(parserLine, LITERAL, JS.N(number.doubleValue() * -1));
+ break;
+ }
+ case LP: { // grouping (not calling)
+ startExpr(b, -1);
+ consume(RP);
+ break;
+ }
+ case INC: case DEC: { // prefix (not postfix)
+ startExpr(b, precedence[tok]);
+ int prev = b.size - 1;
+ if (b.get(prev) == GET && b.getArg(prev) != null)
+ b.set(prev, LITERAL, b.getArg(prev));
+ else if(b.get(prev) == GET)
+ b.pop();
+ else
+ throw pe("prefixed increment/decrement can only be performed on a valid assignment target");
+ b.add(parserLine, GET_PRESERVE, Boolean.TRUE);
+ b.add(parserLine, LITERAL, JS.N(1));
+ b.add(parserLine, tok == INC ? ADD : SUB, JS.N(2));
+ b.add(parserLine, PUT, null);
+ b.add(parserLine, SWAP, null);
+ b.add(parserLine, POP, null);
+ break;
+ }
+ case BANG: case BITNOT: case TYPEOF: {
+ startExpr(b, precedence[tok]);
+ b.add(parserLine, tok);
+ break;
+ }
+ case LC: { // object constructor
+ b.add(parserLine, OBJECT, null); // put an object on the stack
+ if (peekToken() != RC)
+ while(true) {
+ if (peekToken() != NAME && peekToken() != STRING)
+ throw pe("expected NAME or STRING");
+ getToken();
+ b.add(parserLine, LITERAL, string); // grab the key
+ consume(COLON);
+ startExpr(b, NO_COMMA); // grab the value
+ b.add(parserLine, PUT); // put the value into the object
+ b.add(parserLine, POP); // discard the remaining value
+ if (peekToken() == RC) break;
+ consume(COMMA);
+ if (peekToken() == RC) break; // we permit {,,} -- I'm not sure if ECMA does
+ }
+ consume(RC);
+ break;
+ }
+ case NAME: {
+ b.add(parserLine, TOPSCOPE);
+ b.add(parserLine, LITERAL, string);
+ continueExprAfterAssignable(b,minPrecedence);
+ break;
+ }
+ case FUNCTION: {
+ consume(LP);
+ int numArgs = 0;
+ JSFunction b2 = new JSFunction(sourceName, parserLine, null);
+ b.add(parserLine, NEWFUNCTION, b2);
+
+ // function prelude; arguments array is already on the stack
+ b2.add(parserLine, TOPSCOPE);
+ b2.add(parserLine, SWAP);
+ b2.add(parserLine, DECLARE, "arguments"); // declare arguments (equivalent to 'var arguments;')
+ b2.add(parserLine, SWAP); // set this.arguments and leave the value on the stack
+ b2.add(parserLine, PUT);
+
+ while(peekToken() != RP) { // run through the list of argument names
+ numArgs++;
+ if (peekToken() == NAME) {
+ consume(NAME); // a named argument
+ String varName = string;
+
+ b2.add(parserLine, DUP); // dup the args array
+ b2.add(parserLine, GET, JS.N(numArgs - 1)); // retrieve it from the arguments array
+ b2.add(parserLine, TOPSCOPE);
+ b2.add(parserLine, SWAP);
+ b2.add(parserLine, DECLARE, varName); // declare the name
+ b2.add(parserLine, SWAP);
+ b2.add(parserLine, PUT);
+ b2.add(parserLine, POP); // pop the value
+ b2.add(parserLine, POP); // pop the scope
+ }
+ if (peekToken() == RP) break;
+ consume(COMMA);
+ }
+ consume(RP);
+
+ b2.numFormalArgs = numArgs;
+ b2.add(parserLine, POP); // pop off the arguments array
+ b2.add(parserLine, POP); // pop off TOPSCOPE
+
+ if(peekToken() != LC)
+ throw pe("JSFunctions must have a block surrounded by curly brackets");
+
+ parseBlock(b2, null); // the function body
+
+ b2.add(parserLine, LITERAL, null); // in case we "fall out the bottom", return NULL
+ b2.add(parserLine, RETURN);
+
+ break;
+ }
+ default: throw pe("expected expression, found " + codeToString[tok] + ", which cannot start an expression");
+ }
+
+ // attempt to continue the expression
+ continueExpr(b, minPrecedence);
+ }
+ /*
+ private Grammar parseGrammar(Grammar g) throws IOException {
+ int tok = getToken();
+ if (g != null)
+ switch(tok) {
+ case BITOR: return new Grammar.Alternative(g, parseGrammar(null));
+ case ADD: return parseGrammar(new Grammar.Repetition(g, 1, Integer.MAX_VALUE));
+ case MUL: return parseGrammar(new Grammar.Repetition(g, 0, Integer.MAX_VALUE));
+ case HOOK: return parseGrammar(new Grammar.Repetition(g, 0, 1));
+ }
+ Grammar g0 = null;
+ switch(tok) {
+ //case NUMBER: g0 = new Grammar.Literal(number); break;
+ case NAME: g0 = new Grammar.Reference(string); break;
+ case STRING:
+ g0 = new Grammar.Literal(string);
+ if (peekToken() == DOT) {
+ String old = string;
+ consume(DOT);
+ consume(DOT);
+ consume(STRING);
+ if (old.length() != 1 || string.length() != 1) throw pe("literal ranges must be single-char strings");
+ g0 = new Grammar.Range(old.charAt(0), string.charAt(0));
+ }
+ break;
+ case LP: g0 = parseGrammar(null); consume(RP); break;
+ default: pushBackToken(); return g;
+ }
+ if (g == null) return parseGrammar(g0);
+ return parseGrammar(new Grammar.Juxtaposition(g, g0));
+ }
+ */
+ /**
+ * Assuming that a complete assignable (lvalue) has just been
+ * parsed and the object and key are on the stack,
+ * continueExprAfterAssignable will attempt to parse an
+ * expression that modifies the assignable. This method always
+ * decreases the stack depth by exactly one element.
+ */
+ private void continueExprAfterAssignable(JSFunction b,int minPrecedence) throws IOException {
+ int saveParserLine = parserLine;
+ _continueExprAfterAssignable(b,minPrecedence);
+ parserLine = saveParserLine;
+ }
+ private void _continueExprAfterAssignable(JSFunction b,int minPrecedence) throws IOException {
+ if (b == null) throw new Error("got null b; this should never happen");
+ int tok = getToken();
+ if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok])))
+ // force the default case
+ tok = -1;
+ switch(tok) {
+ /*
+ case GRAMMAR: {
+ b.add(parserLine, GET_PRESERVE);
+ Grammar g = parseGrammar(null);
+ if (peekToken() == LC) {
+ g.action = new JSFunction(sourceName, parserLine, null);
+ parseBlock((JSFunction)g.action);
+ ((JSFunction)g.action).add(parserLine, LITERAL, null); // in case we "fall out the bottom", return NULL
+ ((JSFunction)g.action).add(parserLine, RETURN);
+ }
+ b.add(parserLine, MAKE_GRAMMAR, g);
+ b.add(parserLine, PUT);
+ break;
+ }
+ */
+ case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH:
+ case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: case ASSIGN_ADD: case ASSIGN_SUB: case ADD_TRAP: case DEL_TRAP: {
+ if (tok != ADD_TRAP && tok != DEL_TRAP) b.add(parserLine, GET_PRESERVE);
+
+ startExpr(b, precedence[tok]);
+
+ if (tok != ADD_TRAP && tok != DEL_TRAP) {
+ // tok-1 is always s/^ASSIGN_// (0 is BITOR, 1 is ASSIGN_BITOR, etc)
+ b.add(parserLine, tok - 1, tok-1==ADD ? JS.N(2) : null);
+ b.add(parserLine, PUT);
+ b.add(parserLine, SWAP);
+ b.add(parserLine, POP);
+ } else {
+ b.add(parserLine, tok);
+ }
+ break;
+ }
+ case INC: case DEC: { // postfix
+ b.add(parserLine, GET_PRESERVE, Boolean.TRUE);
+ b.add(parserLine, LITERAL, JS.N(1));
+ b.add(parserLine, tok == INC ? ADD : SUB, JS.N(2));
+ b.add(parserLine, PUT, null);
+ b.add(parserLine, SWAP, null);
+ b.add(parserLine, POP, null);
+ b.add(parserLine, LITERAL, JS.N(1));
+ b.add(parserLine, tok == INC ? SUB : ADD, JS.N(2)); // undo what we just did, since this is postfix
+ break;
+ }
+ case ASSIGN: {
+ startExpr(b, precedence[tok]);
+ b.add(parserLine, PUT);
+ b.add(parserLine, SWAP);
+ b.add(parserLine, POP);
+ break;
+ }
+ case LP: {
+
+ // Method calls are implemented by doing a GET_PRESERVE
+ // first. If the object supports method calls, it will
+ // return JS.METHOD
+ int n = parseArgs(b, 2);
+ b.add(parserLine, GET_PRESERVE);
+ b.add(parserLine, CALLMETHOD, JS.N(n));
+ break;
+ }
+ default: {
+ pushBackToken();
+ if(b.get(b.size-1) == LITERAL && b.getArg(b.size-1) != null)
+ b.set(b.size-1,GET,b.getArg(b.size-1));
+ else
+ b.add(parserLine, GET);
+ return;
+ }
+ }
+ }
+
+
+ /**
+ * Assuming that a complete expression has just been parsed,
+ * continueExpr will attempt to extend this expression by
+ * parsing additional tokens and appending additional bytecodes.
+ *
+ * No operators with precedence less than minPrecedence
+ * will be parsed.
+ *
+ * If any bytecodes are appended, they will not alter the stack
+ * depth.
+ */
+ private void continueExpr(JSFunction b, int minPrecedence) throws IOException {
+ int saveParserLine = parserLine;
+ _continueExpr(b, minPrecedence);
+ parserLine = saveParserLine;
+ }
+ private void _continueExpr(JSFunction b, int minPrecedence) throws IOException {
+ if (b == null) throw new Error("got null b; this should never happen");
+ int tok = getToken();
+ if (tok == -1) return;
+ if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))) {
+ pushBackToken();
+ return;
+ }
+
+ switch (tok) {
+ case LP: { // invocation (not grouping)
+ int n = parseArgs(b, 1);
+ b.add(parserLine, CALL, JS.N(n));
+ break;
+ }
+ case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
+ case RSH: case URSH: case MUL: case DIV: case MOD:
+ case GT: case GE: case EQ: case NE: case LT: case LE: case SUB: {
+ startExpr(b, precedence[tok]);
+ b.add(parserLine, tok);
+ break;
+ }
+ case ADD: {
+ int count=1;
+ int nextTok;
+ do {
+ startExpr(b,precedence[tok]);
+ count++;
+ nextTok = getToken();
+ } while(nextTok == tok);
+ pushBackToken();
+ b.add(parserLine, tok, JS.N(count));
+ break;
+ }
+ case OR: case AND: {
+ b.add(parserLine, tok == AND ? JSFunction.JF : JSFunction.JT, JS.ZERO); // test to see if we can short-circuit
+ int size = b.size;
+ startExpr(b, precedence[tok]); // otherwise check the second value
+ b.add(parserLine, JMP, JS.N(2)); // leave the second value on the stack and jump to the end
+ b.add(parserLine, LITERAL, tok == AND ?
+ JS.B(false) : JS.B(true)); // target of the short-circuit jump is here
+ b.set(size - 1, JS.N(b.size - size)); // write the target of the short-circuit jump
+ break;
+ }
+ case DOT: {
+ // support foo..bar syntax for foo[""].bar
+ if (peekToken() == DOT) {
+ string = "";
+ } else {
+ consume(NAME);
+ }
+ b.add(parserLine, LITERAL, string);
+ continueExprAfterAssignable(b,minPrecedence);
+ break;
+ }
+ case LB: { // subscripting (not array constructor)
+ startExpr(b, -1);
+ consume(RB);
+ continueExprAfterAssignable(b,minPrecedence);
+ break;
+ }
+ case HOOK: {
+ b.add(parserLine, JF, JS.ZERO); // jump to the if-false expression
+ int size = b.size;
+ startExpr(b, minPrecedence); // write the if-true expression
+ b.add(parserLine, JMP, JS.ZERO); // if true, jump *over* the if-false expression
+ b.set(size - 1, JS.N(b.size - size + 1)); // now we know where the target of the jump is
+ consume(COLON);
+ size = b.size;
+ startExpr(b, minPrecedence); // write the if-false expression
+ b.set(size - 1, JS.N(b.size - size + 1)); // this is the end; jump to here
+ break;
+ }
+ case COMMA: {
+ // pop the result of the previous expression, it is ignored
+ b.add(parserLine,POP);
+ startExpr(b,-1);
+ break;
+ }
+ default: {
+ pushBackToken();
+ return;
+ }
+ }
+
+ continueExpr(b, minPrecedence); // try to continue the expression
+ }
+
+ // parse a set of comma separated function arguments, assume LP has already been consumed
+ // if swap is true, (because the function is already on the stack) we will SWAP after each argument to keep it on top
+ private int parseArgs(JSFunction b, int pushdown) throws IOException {
+ int i = 0;
+ while(peekToken() != RP) {
+ i++;
+ if (peekToken() != COMMA) {
+ startExpr(b, NO_COMMA);
+ b.add(parserLine, SWAP, JS.N(pushdown));
+ if (peekToken() == RP) break;
+ }
+ consume(COMMA);
+ }
+ consume(RP);
+ return i;
+ }
+
+ /** Parse a block of statements which must be surrounded by LC..RC. */
+ void parseBlock(JSFunction b) throws IOException { parseBlock(b, null); }
+ void parseBlock(JSFunction b, String label) throws IOException {
+ int saveParserLine = parserLine;
+ _parseBlock(b, label);
+ parserLine = saveParserLine;
+ }
+ void _parseBlock(JSFunction b, String label) throws IOException {
+ if (peekToken() == -1) return;
+ else if (peekToken() != LC) parseStatement(b, null);
+ else {
+ consume(LC);
+ while(peekToken() != RC && peekToken() != -1) parseStatement(b, null);
+ consume(RC);
+ }
+ }
+
+ /** Parse a single statement, consuming the RC or SEMI which terminates it. */
+ void parseStatement(JSFunction b, String label) throws IOException {
+ int saveParserLine = parserLine;
+ _parseStatement(b, label);
+ parserLine = saveParserLine;
+ }
+ void _parseStatement(JSFunction b, String label) throws IOException {
+ int tok = peekToken();
+ if (tok == -1) return;
+ switch(tok = getToken()) {
+
+ case THROW: case ASSERT: case RETURN: {
+ if (tok == RETURN && peekToken() == SEMI)
+ b.add(parserLine, LITERAL, null);
+ else
+ startExpr(b, -1);
+ b.add(parserLine, tok);
+ consume(SEMI);
+ break;
+ }
+ case BREAK: case CONTINUE: {
+ if (peekToken() == NAME) consume(NAME);
+ b.add(parserLine, tok, string);
+ consume(SEMI);
+ break;
+ }
+ case VAR: {
+ b.add(parserLine, TOPSCOPE); // push the current scope
+ while(true) {
+ consume(NAME);
+ b.add(parserLine, DECLARE, string); // declare it
+ if (peekToken() == ASSIGN) { // if there is an '=' after the variable name
+ consume(ASSIGN);
+ startExpr(b, NO_COMMA);
+ b.add(parserLine, PUT); // assign it
+ b.add(parserLine, POP); // clean the stack
+ } else {
+ b.add(parserLine, POP); // pop the string pushed by declare
+ }
+ if (peekToken() != COMMA) break;
+ consume(COMMA);
+ }
+ b.add(parserLine, POP); // pop off the topscope
+ if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
+ break;
+ }
+ case IF: {
+ consume(LP);
+ startExpr(b, -1);
+ consume(RP);
+
+ b.add(parserLine, JF, JS.ZERO); // if false, jump to the else-block
+ int size = b.size;
+ parseStatement(b, null);
+
+ if (peekToken() == ELSE) {
+ consume(ELSE);
+ b.add(parserLine, JMP, JS.ZERO); // if we took the true-block, jump over the else-block
+ b.set(size - 1, JS.N(b.size - size + 1));
+ size = b.size;
+ parseStatement(b, null);
+ }
+ b.set(size - 1, JS.N(b.size - size + 1)); // regardless of which branch we took, b[size] needs to point here
+ break;
+ }
+ case WHILE: {
+ consume(LP);
+ if (label != null) b.add(parserLine, LABEL, label);
+ b.add(parserLine, LOOP);
+ int size = b.size;
+ b.add(parserLine, POP); // discard the first-iteration indicator
+ startExpr(b, -1);
+ b.add(parserLine, JT, JS.N(2)); // if the while() clause is true, jump over the BREAK
+ b.add(parserLine, BREAK);
+ consume(RP);
+ parseStatement(b, null);
+ b.add(parserLine, CONTINUE); // if we fall out of the end, definately continue
+ b.set(size - 1, JS.N(b.size - size + 1)); // end of the loop
+ break;
+ }
+ case SWITCH: {
+ consume(LP);
+ if (label != null) b.add(parserLine, LABEL, label);
+ b.add(parserLine, LOOP);
+ int size0 = b.size;
+ startExpr(b, -1);
+ consume(RP);
+ consume(LC);
+ while(true)
+ if (peekToken() == CASE) { // we compile CASE statements like a bunch of if..else's
+ consume(CASE);
+ b.add(parserLine, DUP); // duplicate the switch() value; we'll consume one copy
+ startExpr(b, -1);
+ consume(COLON);
+ b.add(parserLine, EQ); // check if we should do this case-block
+ b.add(parserLine, JF, JS.ZERO); // if not, jump to the next one
+ int size = b.size;
+ while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) parseStatement(b, null);
+ b.set(size - 1, JS.N(1 + b.size - size));
+ } else if (peekToken() == DEFAULT) {
+ consume(DEFAULT);
+ consume(COLON);
+ while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) parseStatement(b, null);
+ } else if (peekToken() == RC) {
+ consume(RC);
+ b.add(parserLine, BREAK); // break out of the loop if we 'fall through'
+ break;
+ } else {
+ throw pe("expected CASE, DEFAULT, or RC; got " + codeToString[peekToken()]);
+ }
+ b.set(size0 - 1, JS.N(b.size - size0 + 1)); // end of the loop
+ break;
+ }
+
+ case DO: {
+ if (label != null) b.add(parserLine, LABEL, label);
+ b.add(parserLine, LOOP);
+ int size = b.size;
+ parseStatement(b, null);
+ consume(WHILE);
+ consume(LP);
+ startExpr(b, -1);
+ b.add(parserLine, JT, JS.N(2)); // check the while() clause; jump over the BREAK if true
+ b.add(parserLine, BREAK);
+ b.add(parserLine, CONTINUE);
+ consume(RP);
+ consume(SEMI);
+ b.set(size - 1, JS.N(b.size - size + 1)); // end of the loop; write this location to the LOOP instruction
+ break;
+ }
+
+ case TRY: {
+ b.add(parserLine, TRY); // try bytecode causes a TryMarker to be pushed
+ int tryInsn = b.size - 1;
+ // parse the expression to be TRYed
+ parseStatement(b, null);
+ // pop the try marker. this is pushed when the TRY bytecode is executed
+ b.add(parserLine, POP);
+ // jump forward to the end of the catch block, start of the finally block
+ b.add(parserLine, JMP);
+ int successJMPInsn = b.size - 1;
+
+ if (peekToken() != CATCH && peekToken() != FINALLY)
+ throw pe("try without catch or finally");
+
+ int catchJMPDistance = -1;
+ if (peekToken() == CATCH) {
+ Vec catchEnds = new Vec();
+ boolean catchAll = false;
+
+ catchJMPDistance = b.size - tryInsn;
+
+ while(peekToken() == CATCH && !catchAll) {
+ String exceptionVar;
+ getToken();
+ consume(LP);
+ consume(NAME);
+ exceptionVar = string;
+ int[] writebacks = new int[] { -1, -1, -1 };
+ if (peekToken() != RP) {
+ // extended Ibex catch block: catch(e faultCode "foo.bar.baz")
+ consume(NAME);
+ b.add(parserLine, DUP);
+ b.add(parserLine, LITERAL, string);
+ b.add(parserLine, GET);
+ b.add(parserLine, DUP);
+ b.add(parserLine, LITERAL, null);
+ b.add(parserLine, EQ);
+ b.add(parserLine, JT);
+ writebacks[0] = b.size - 1;
+ if (peekToken() == STRING) {
+ consume(STRING);
+ b.add(parserLine, DUP);
+ b.add(parserLine, LITERAL, string);
+ b.add(parserLine, LT);
+ b.add(parserLine, JT);
+ writebacks[1] = b.size - 1;
+ b.add(parserLine, DUP);
+ b.add(parserLine, LITERAL, string + "/"); // (slash is ASCII after dot)
+ b.add(parserLine, GE);
+ b.add(parserLine, JT);
+ writebacks[2] = b.size - 1;
+ } else {
+ consume(NUMBER);
+ b.add(parserLine, DUP);
+ b.add(parserLine, LITERAL, number);
+ b.add(parserLine, EQ);
+ b.add(parserLine, JF);
+ writebacks[1] = b.size - 1;
+ }
+ b.add(parserLine, POP); // pop the element thats on the stack from the compare
+ } else {
+ catchAll = true;
+ }
+ consume(RP);
+ // the exception is on top of the stack; put it to the chosen name
+ b.add(parserLine, NEWSCOPE);
+ b.add(parserLine, TOPSCOPE);
+ b.add(parserLine, SWAP);
+ b.add(parserLine, LITERAL,exceptionVar);
+ b.add(parserLine, DECLARE);
+ b.add(parserLine, SWAP);
+ b.add(parserLine, PUT);
+ b.add(parserLine, POP);
+ b.add(parserLine, POP);
+ parseBlock(b, null);
+ b.add(parserLine, OLDSCOPE);
+
+ b.add(parserLine, JMP);
+ catchEnds.addElement(new Integer(b.size-1));
+
+ for(int i=0; i<3; i++) if (writebacks[i] != -1) b.set(writebacks[i], JS.N(b.size-writebacks[i]));
+ b.add(parserLine, POP); // pop the element thats on the stack from the compare
+ }
+
+ if(!catchAll)
+ b.add(parserLine, THROW);
+
+ for(int i=0;i= 0 jump 5 down (to NEWSCOPE)
+ // Move the LoopMarker back into place - this is sort of ugly
+ b.add(parserLine, SWAP, JS.N(3));
+ b.add(parserLine, SWAP, JS.N(3));
+ b.add(parserLine, SWAP, JS.N(3));
+ // Stack is now: LoopMarker, -1, keys, obj, ...
+ b.add(parserLine, BREAK);
+
+ b.add(parserLine, NEWSCOPE);
+ if(hadVar) {
+ b.add(parserLine, DECLARE, varName);
+ b.add(parserLine, POP);
+ }
+
+ // Stack is now: index, keys, obj, LoopMarker, ...
+ b.add(parserLine, GET_PRESERVE); // key, index, keys, obj, LoopMarker, ...
+ b.add(parserLine, TOPSCOPE); // scope, key, index, keys, obj, LoopMarker, ...
+ b.add(parserLine, SWAP); // key, scope, index, keys, obj, LoopMarker, ...
+ b.add(parserLine, LITERAL, varName); // varName, key, scope, index, keys, obj, LoopMaker, ...
+ b.add(parserLine, SWAP); // key, varName, scope, index, keys, obj, LoopMarker, ...
+ b.add(parserLine, PUT); // key, scope, index, keys, obj, LoopMarker, ...
+ b.add(parserLine, POP); // scope, index, keys, obj, LoopMarker
+ b.add(parserLine, POP); // index, keys, obj, LoopMarker, ...
+ // Move the LoopMarker back into place - this is sort of ugly
+ b.add(parserLine, SWAP, JS.N(3));
+ b.add(parserLine, SWAP, JS.N(3));
+ b.add(parserLine, SWAP, JS.N(3));
+
+ parseStatement(b, null);
+
+ b.add(parserLine, OLDSCOPE);
+ b.add(parserLine, CONTINUE);
+ // jump here on break
+ b.set(size, JS.N(b.size - size));
+
+ b.add(parserLine, POP); // N
+ b.add(parserLine, POP); // KEYS
+ b.add(parserLine, POP); // OBJ
+
+ } else {
+ if (hadVar) pushBackToken(VAR, null); // yeah, this actually matters
+ b.add(parserLine, NEWSCOPE); // grab a fresh scope
+
+ parseStatement(b, null); // initializer
+ JSFunction e2 = // we need to put the incrementor before the test
+ new JSFunction(sourceName, parserLine, null); // so we save the test here
+ if (peekToken() != SEMI)
+ startExpr(e2, -1);
+ else
+ e2.add(parserLine, JSFunction.LITERAL, Boolean.TRUE); // handle the for(foo;;foo) case
+ consume(SEMI);
+ if (label != null) b.add(parserLine, LABEL, label);
+ b.add(parserLine, LOOP);
+ int size2 = b.size;
+
+ b.add(parserLine, JT, JS.ZERO); // if we're on the first iteration, jump over the incrementor
+ int size = b.size;
+ if (peekToken() != RP) { // do the increment thing
+ startExpr(b, -1);
+ b.add(parserLine, POP);
+ }
+ b.set(size - 1, JS.N(b.size - size + 1));
+ consume(RP);
+
+ b.paste(e2); // ok, *now* test if we're done yet
+ b.add(parserLine, JT, JS.N(2)); // break out if we don't meet the test
+ b.add(parserLine, BREAK);
+ parseStatement(b, null);
+ b.add(parserLine, CONTINUE); // if we fall out the bottom, CONTINUE
+ b.set(size2 - 1, JS.N(b.size - size2 + 1)); // end of the loop
+
+ b.add(parserLine, OLDSCOPE); // get our scope back
+ }
+ break;
+ }
+
+ case NAME: { // either a label or an identifier; this is the one place we're not LL(1)
+ String possiblyTheLabel = string;
+ if (peekToken() == COLON) { // label
+ consume(COLON);
+ parseStatement(b, possiblyTheLabel);
+ break;
+ } else { // expression
+ pushBackToken(NAME, possiblyTheLabel);
+ startExpr(b, -1);
+ b.add(parserLine, POP);
+ if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
+ break;
+ }
+ }
+
+ case SEMI: return; // yep, the null statement is valid
+
+ case LC: { // blocks are statements too
+ pushBackToken();
+ b.add(parserLine, NEWSCOPE);
+ parseBlock(b, label);
+ b.add(parserLine, OLDSCOPE);
+ break;
+ }
+
+ default: { // hope that it's an expression
+ pushBackToken();
+ startExpr(b, -1);
+ b.add(parserLine, POP);
+ if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
+ break;
+ }
+ }
+ }
+
+
+ // ParserException //////////////////////////////////////////////////////////////////////
+ private IOException pe(String s) { return new IOException(sourceName + ":" + line + " " + s); }
+
+}
+
diff --git a/upstream/org.ibex.core/src/org/ibex/js/Stream.java b/upstream/org.ibex.core/src/org/ibex/js/Stream.java
new file mode 100644
index 0000000..05045e9
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/js/Stream.java
@@ -0,0 +1,157 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+import java.io.*;
+import java.util.zip.*;
+import org.ibex.util.*;
+import org.ibex.plat.*;
+import org.ibex.net.*;
+
+/**
+ * Essentiall an InputStream "factory". You can repeatedly ask a
+ * Stream for an InputStream, and each InputStream you get back will
+ * be totally independent of the others (ie separate stream position
+ * and state) although they draw from the same data source.
+ */
+public abstract class Stream extends JS.Cloneable {
+
+ // Public Interface //////////////////////////////////////////////////////////////////////////////
+
+ public static InputStream getInputStream(Object js) throws IOException { return ((Stream)((JS)js).unclone()).getInputStream();}
+ public static class NotCacheableException extends Exception { }
+
+ // streams are "sealed" by default to prevent accidental object leakage
+ public void put(Object key, Object val) { }
+ private Cache getCache = new Cache(100);
+ protected Object _get(Object key) { return null; }
+ public final Object get(Object key) {
+ Object ret = getCache.get(key);
+ if (ret == null) getCache.put(key, ret = _get(key));
+ return ret;
+ }
+
+ // Private Interface //////////////////////////////////////////////////////////////////////////////
+
+ public abstract InputStream getInputStream() throws IOException;
+ protected String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); }
+
+ /** HTTP or HTTPS resource */
+ public static class HTTP extends Stream {
+ private String url;
+ public String toString() { return "Stream.HTTP:" + url; }
+ public HTTP(String url) { while (url.endsWith("/")) url = url.substring(0, url.length() - 1); this.url = url; }
+ public Object _get(Object key) { return new HTTP(url + "/" + (String)key); }
+ public String getCacheKey(Vec path) throws NotCacheableException { return url; }
+ public InputStream getInputStream() throws IOException { return new org.ibex.net.HTTP(url).GET(); }
+ }
+
+ /** byte arrays */
+ public static class ByteArray extends Stream {
+ private byte[] bytes;
+ private String cacheKey;
+ public ByteArray(byte[] bytes, String cacheKey) { this.bytes = bytes; this.cacheKey = cacheKey; }
+ public String getCacheKey() throws NotCacheableException {
+ if (cacheKey == null) throw new NotCacheableException(); return cacheKey; }
+ public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(bytes); }
+ }
+
+ /** a file */
+ public static class File extends Stream {
+ private String path;
+ public File(String path) { this.path = path; }
+ public String toString() { return "file:" + path; }
+ public String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); /* already on disk */ }
+ public InputStream getInputStream() throws IOException { return new FileInputStream(path); }
+ public Object _get(Object key) { return new File(path + java.io.File.separatorChar + (String)key); }
+ }
+
+ /** "unwrap" a Zip archive */
+ public static class Zip extends Stream {
+ private Stream parent;
+ private String path;
+ public Zip(Stream parent) { this(parent, null); }
+ public Zip(Stream parent, String path) {
+ while(path != null && path.startsWith("/")) path = path.substring(1);
+ this.parent = parent;
+ this.path = path;
+ }
+ public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!zip:"; }
+ public Object _get(Object key) { return new Zip(parent, path==null?(String)key:path+'/'+(String)key); }
+ public InputStream getInputStream() throws IOException {
+ InputStream pis = parent.getInputStream();
+ ZipInputStream zis = new ZipInputStream(pis);
+ ZipEntry ze = zis.getNextEntry();
+ while(ze != null && !ze.getName().equals(path)) ze = zis.getNextEntry();
+ if (ze == null) throw new IOException("requested file (" + path + ") not found in archive");
+ return new KnownLength.KnownLengthInputStream(zis, (int)ze.getSize());
+ }
+ }
+
+ /** "unwrap" a Cab archive */
+ public static class Cab extends Stream {
+ private Stream parent;
+ private String path;
+ public Cab(Stream parent) { this(parent, null); }
+ public Cab(Stream parent, String path) { this.parent = parent; this.path = path; }
+ public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!cab:"; }
+ public Object _get(Object key) { return new Cab(parent, path==null?(String)key:path+'/'+(String)key); }
+ public InputStream getInputStream() throws IOException { return new MSPack(parent.getInputStream()).getInputStream(path); }
+ }
+
+ /** the Builtin resource */
+ public static class Builtin extends Stream {
+ public String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); }
+ public InputStream getInputStream() throws IOException { return Platform.getBuiltinInputStream(); }
+ }
+
+ /** shadow resource which replaces the graft */
+ public static class ProgressWatcher extends Stream {
+ final Stream watchee;
+ JS callback;
+ public ProgressWatcher(Stream watchee, JS callback) { this.watchee = watchee; this.callback = callback; }
+ public String getCacheKey() throws NotCacheableException { return watchee.getCacheKey(); }
+ public InputStream getInputStream() throws IOException {
+ final InputStream is = watchee.getInputStream();
+ return new FilterInputStream(is) {
+ int bytesDownloaded = 0;
+ public int read() throws IOException {
+ int ret = super.read();
+ if (ret != -1) bytesDownloaded++;
+ return ret;
+ }
+ public int read(byte[] b, int off, int len) throws IOException {
+ int ret = super.read(b, off, len);
+ if (ret != 1) bytesDownloaded += ret;
+ Scheduler.add(new Task() { public void perform() throws IOException, JSExn {
+ callback.call(N(bytesDownloaded),
+ N(is instanceof KnownLength ? ((KnownLength)is).getLength() : 0), null, null, 2);
+ } });
+ return ret;
+ }
+ };
+ }
+ }
+
+ /** subclass from this if you want a CachedInputStream for each path */
+ public static class CachedStream extends Stream {
+ private Stream parent;
+ private boolean disk = false;
+ private String key;
+ public String getCacheKey() throws NotCacheableException { return key; }
+ CachedInputStream cis = null;
+ public CachedStream(Stream p, String s, boolean d) throws NotCacheableException {
+ this.parent = p; this.disk = d; this.key = p.getCacheKey();
+ }
+ public InputStream getInputStream() throws IOException {
+ if (cis != null) return cis.getInputStream();
+ if (!disk) {
+ cis = new CachedInputStream(parent.getInputStream());
+ } else {
+ java.io.File f = org.ibex.core.LocalStorage.Cache.getCacheFileForKey(key);
+ if (f.exists()) return new FileInputStream(f);
+ cis = new CachedInputStream(parent.getInputStream(), f);
+ }
+ return cis.getInputStream();
+ }
+ }
+}
diff --git a/upstream/org.ibex.core/src/org/ibex/js/Tokens.java b/upstream/org.ibex.core/src/org/ibex/js/Tokens.java
new file mode 100644
index 0000000..8970e14
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/js/Tokens.java
@@ -0,0 +1,120 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+/** this class contains a public static final int for each valid token */
+interface Tokens {
+
+ // Token Constants //////////////////////////////////////////////////////////
+
+ // arithmetic operations; also valid as bytecodes
+ public static final int BITOR = 0; // |
+ public static final int ASSIGN_BITOR = 1; // |=
+ public static final int BITXOR = 2; // ^
+ public static final int ASSIGN_BITXOR = 3; // ^=
+ public static final int BITAND = 4; // &
+ public static final int ASSIGN_BITAND = 5; // &=
+ public static final int LSH = 6; // <<
+ public static final int ASSIGN_LSH = 7; // <<=
+ public static final int RSH = 8; // >>
+ public static final int ASSIGN_RSH = 9; // >>=
+ public static final int URSH = 10; // >>>
+ public static final int ASSIGN_URSH = 11; // >>>=
+ public static final int ADD = 12; // +
+ public static final int ASSIGN_ADD = 13; // +=
+ public static final int SUB = 14; // -
+ public static final int ASSIGN_SUB = 15; // -=
+ public static final int MUL = 16; // *
+ public static final int ASSIGN_MUL = 17; // *=
+ public static final int DIV = 18; // /
+ public static final int ASSIGN_DIV = 19; // /=
+ public static final int MOD = 20; // %
+ public static final int ASSIGN_MOD = 21; // %=
+ public static final int BITNOT = 22; // ~
+ public static final int ASSIGN_BITNOT = 23; // ~=
+
+ // logical operations; also valid as bytecodes
+ public static final int OR = 24; // ||
+ public static final int AND = 25; // &&
+ public static final int BANG = 26; // !
+
+ // equality operations; also valid as bytecodes
+ public static final int EQ = 27; // ==
+ public static final int NE = 28; // !=
+ public static final int LT = 29; // <
+ public static final int LE = 30; // <=
+ public static final int GT = 31; // >
+ public static final int GE = 32; // >=
+ public static final int SHEQ = 33; // ===
+ public static final int SHNE = 34; // !==
+
+ // other permissible bytecode tokens
+ public static final int RETURN = 35; // return
+ public static final int TYPEOF = 36; // typeof
+ public static final int BREAK = 37; // break keyword
+ public static final int CONTINUE = 38; // continue keyword
+ public static final int TRY = 39; // try
+ public static final int THROW = 40; // throw
+ public static final int ASSERT = 41; // assert keyword
+
+ public static final int NAME = 42; // *** identifiers ***
+ public static final int NUMBER = 43; // *** numeric literals ***
+ public static final int STRING = 44; // *** string literals ***
+ public static final int NULL = 45; // null
+ public static final int THIS = 46; // this
+ public static final int FALSE = 47; // false
+ public static final int TRUE = 48; // true
+ public static final int IN = 49; // in
+
+ public static final int SEMI = 50; // ;
+ public static final int LB = 51; // [
+ public static final int RB = 52; // ]
+ public static final int LC = 53; // {
+ public static final int RC = 54; // }
+ public static final int LP = 55; // (
+ public static final int RP = 56; // )
+ public static final int COMMA = 57; // ,
+ public static final int ASSIGN = 58; // =
+ public static final int HOOK = 59; // ?
+ public static final int COLON = 60; // :
+ public static final int INC = 61; // ++
+ public static final int DEC = 62; // --
+ public static final int DOT = 63; // .
+ public static final int FUNCTION = 64; // function
+ public static final int IF = 65; // if keyword
+ public static final int ELSE = 66; // else keyword
+ public static final int SWITCH = 67; // switch keyword
+ public static final int CASE = 68; // case keyword
+ public static final int DEFAULT = 69; // default keyword
+ public static final int WHILE = 70; // while keyword
+ public static final int DO = 71; // do keyword
+ public static final int FOR = 72; // for keyword
+ public static final int VAR = 73; // var keyword
+ public static final int WITH = 74; // with keyword
+ public static final int CATCH = 75; // catch keyword
+ public static final int FINALLY = 76; // finally keyword
+ public static final int RESERVED = 77; // reserved keyword
+ public static final int GRAMMAR = 78; // the grammar-definition operator (::=)
+ public static final int ADD_TRAP = 79; // the add-trap operator (++=)
+ public static final int DEL_TRAP = 80; // the del-trap operator (--=)
+
+ public static final int MAX_TOKEN = DEL_TRAP;
+
+ public final static String[] codeToString = new String[] {
+ "BITOR", "ASSIGN_BITOR", "BITXOR", "ASSIGN_BITXOR", "BITAND",
+ "ASSIGN_BITAND", "LSH", "ASSIGN_LSH", "RSH", "ASSIGN_RSH",
+ "URSH", "ASSIGN_URSH", "ADD", "ASSIGN_ADD", "SUB",
+ "ASSIGN_SUB", "MUL", "ASSIGN_MUL", "DIV", "ASSIGN_DIV", "MOD",
+ "ASSIGN_MOD", "BITNOT", "ASSIGN_BITNOT", "OR", "AND", "BANG",
+ "EQ", "NE", "LT", "LE", "GT", "GE", "SHEQ", "SHNE", "RETURN",
+ "TYPEOF", "BREAK", "CONTINUE", "TRY", "THROW", "ASSERT", "NAME",
+ "NUMBER", "STRING", "NULL", "THIS", "FALSE", "TRUE", "IN",
+ "SEMI", "LB", "RB", "LC", "RC", "LP", "RP", "COMMA", "ASSIGN",
+ "HOOK", "COLON", "INC", "DEC", "DOT", "FUNCTION", "IF",
+ "ELSE", "SWITCH", "CASE", "DEFAULT", "WHILE", "DO", "FOR",
+ "VAR", "WITH", "CATCH", "FINALLY", "RESERVED", "GRAMMAR",
+ "ADD_TRAP", "DEL_TRAP"
+ };
+
+}
+
+
diff --git a/upstream/org.ibex.core/src/org/ibex/js/Trap.java b/upstream/org.ibex.core/src/org/ibex/js/Trap.java
new file mode 100644
index 0000000..77476f6
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/js/Trap.java
@@ -0,0 +1,61 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+/**
+ * This class encapsulates a single trap placed on a given node. The
+ * traps for a given property name on a given box are maintained as a
+ * linked list stack, with the most recently placed trap at the head
+ * of the list.
+ */
+class Trap {
+
+ JS trapee = null; ///< the box on which this trap was placed
+ Object name = null; ///< the property that the trap was placed on
+
+ JSFunction f = null; ///< the function for this trap
+ Trap next = null; ///< the next trap down the trap stack
+
+ Trap(JS b, String n, JSFunction f, Trap nx) {
+ trapee = b; name = n; this.f = f; this.next = nx;
+ }
+
+ static final JSFunction putInvoker = new JSFunction("putInvoker", 0, null);
+ static final JSFunction getInvoker = new JSFunction("getInvoker", 0, null);
+
+ static {
+ putInvoker.add(1, ByteCodes.PUT, null);
+ putInvoker.add(2, Tokens.RETURN, null);
+ getInvoker.add(1, ByteCodes.GET, null);
+ getInvoker.add(2, Tokens.RETURN, null);
+ }
+
+ void invoke(Object value) throws JSExn {
+ Interpreter i = new Interpreter(putInvoker, false, null);
+ i.stack.push(trapee);
+ i.stack.push(name);
+ i.stack.push(value);
+ i.resume();
+ }
+
+ Object invoke() throws JSExn {
+ Interpreter i = new Interpreter(getInvoker, false, null);
+ i.stack.push(trapee);
+ i.stack.push(name);
+ return i.resume();
+ }
+
+ // FIXME: review; is necessary?
+ static class TrapScope extends JSScope {
+ Trap t;
+ Object val = null;
+ boolean cascadeHappened = false;
+ public TrapScope(JSScope parent, Trap t, Object val) { super(parent); this.t = t; this.val = val; }
+ public Object get(Object key) throws JSExn {
+ if (key.equals("trapee")) return t.trapee;
+ if (key.equals("callee")) return t.f;
+ if (key.equals("trapname")) return t.name;
+ return super.get(key);
+ }
+ }
+}
+
diff --git a/upstream/org.ibex.core/src/org/ibex/net/HTTP.java b/upstream/org.ibex.core/src/org/ibex/net/HTTP.java
new file mode 100644
index 0000000..15ba899
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/net/HTTP.java
@@ -0,0 +1,1303 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.net;
+
+import java.net.*;
+import java.io.*;
+import java.util.*;
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.plat.*;
+import org.ibex.core.*;
+import org.ibex.crypto.*;
+
+/**
+ * This object encapsulates a *single* HTTP connection. Multiple requests may be pipelined over a connection (thread-safe),
+ * although any IOException encountered in a request will invalidate all later requests.
+ */
+public class HTTP {
+
+
+ // Public Methods ////////////////////////////////////////////////////////////////////////////////////////
+
+ public HTTP(String url) { this(url, false); }
+ public HTTP(String url, boolean skipResolveCheck) { originalUrl = url; this.skipResolveCheck = skipResolveCheck; }
+
+ /** Performs an HTTP GET request */
+ public InputStream GET() throws IOException { return makeRequest(null, null); }
+
+ /** Performs an HTTP POST request; content is additional headers, blank line, and body */
+ public InputStream POST(String contentType, String content) throws IOException { return makeRequest(contentType, content); }
+
+ public static class HTTPException extends IOException { public HTTPException(String s) { super(s); } }
+
+ public static HTTP stdio = new HTTP("stdio:");
+
+
+ // Statics ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ static Hash resolvedHosts = new Hash(); ///< cache for resolveAndCheckIfFirewalled()
+ private static Hash authCache = new Hash(); ///< cache of userInfo strings, keyed on originalUrl
+
+
+ // Instance Data ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ final String originalUrl; ///< the URL as passed to the original constructor; this is never changed
+ String url = null; ///< the URL to connect to; this is munged when the url is parsed */
+ String host = null; ///< the host to connect to
+ int port = -1; ///< the port to connect on
+ boolean ssl = false; ///< true if SSL (HTTPS) should be used
+ String path = null; ///< the path (URI) to retrieve on the server
+ Socket sock = null; ///< the socket
+ InputStream in = null; ///< the socket's inputstream
+ String userInfo = null; ///< the username and password portions of the URL
+ boolean firstRequest = true; ///< true iff this is the first request to be made on this socket
+ boolean skipResolveCheck = false; ///< allowed to skip the resolve check when downloading PAC script
+ boolean proxied = false; ///< true iff we're using a proxy
+
+ /** this is null if the current request is the first request on
+ * this HTTP connection; otherwise it is a Semaphore which will be
+ * released once the request ahead of us has recieved its response
+ */
+ Semaphore okToRecieve = null;
+
+ /**
+ * This method isn't synchronized; however, only one thread can be in the inner synchronized block at a time, and the rest of
+ * the method is protected by in-order one-at-a-time semaphore lock-steps
+ */
+ private InputStream makeRequest(String contentType, String content) throws IOException {
+
+ // Step 1: send the request and establish a semaphore to stop any requests that pipeline after us
+ Semaphore blockOn = null;
+ Semaphore releaseMe = null;
+ synchronized(this) {
+ try {
+ connect();
+ sendRequest(contentType, content);
+ } catch (IOException e) {
+ reset();
+ throw e;
+ }
+ blockOn = okToRecieve;
+ releaseMe = okToRecieve = new Semaphore();
+ }
+
+ // Step 2: wait for requests ahead of us to complete, then read the reply off the stream
+ boolean doRelease = true;
+ try {
+ if (blockOn != null) blockOn.block();
+
+ // previous call wrecked the socket connection, but we already sent our request, so we can't just retry --
+ // this could cause the server to receive the request twice, which could be bad (think of the case where the
+ // server call causes Amazon.com to ship you an item with one-click purchasing).
+ if (in == null)
+ throw new HTTPException("a previous pipelined call messed up the socket");
+
+ Hashtable h = in == null ? null : parseHeaders(in);
+ if (h == null) {
+ if (firstRequest) throw new HTTPException("server closed the socket with no response");
+ // sometimes the server chooses to close the stream between requests
+ reset();
+ releaseMe.release();
+ return makeRequest(contentType, content);
+ }
+
+ String reply = h.get("STATUSLINE").toString();
+
+ if (reply.startsWith("407") || reply.startsWith("401")) {
+
+ if (reply.startsWith("407")) doProxyAuth(h, content == null ? "GET" : "POST");
+ else doWebAuth(h, content == null ? "GET" : "POST");
+
+ if (h.get("HTTP").equals("1.0") && h.get("content-length") == null) {
+ if (Log.on) Log.info(this, "proxy returned an HTTP/1.0 reply with no content-length...");
+ reset();
+ } else {
+ int cl = h.get("content-length") == null ? -1 : Integer.parseInt(h.get("content-length").toString());
+ new HTTPInputStream(in, cl, releaseMe).close();
+ }
+ releaseMe.release();
+ return makeRequest(contentType, content);
+
+ } else if (reply.startsWith("2")) {
+ if (h.get("HTTP").equals("1.0") && h.get("content-length") == null)
+ throw new HTTPException("Ibex does not support HTTP/1.0 servers which fail to return the Content-Length header");
+ int cl = h.get("content-length") == null ? -1 : Integer.parseInt(h.get("content-length").toString());
+ InputStream ret = new HTTPInputStream(in, cl, releaseMe);
+ if ("gzip".equals(h.get("content-encoding"))) ret = new java.util.zip.GZIPInputStream(ret);
+ doRelease = false;
+ return ret;
+
+ } else {
+ throw new HTTPException("HTTP Error: " + reply);
+
+ }
+
+ } catch (IOException e) { reset(); throw e;
+ } finally { if (doRelease) releaseMe.release();
+ }
+ }
+
+
+ // Safeguarded DNS Resolver ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ * resolves the hostname and returns it as a string in the form "x.y.z.w"
+ * @throws HTTPException if the host falls within a firewalled netblock
+ */
+ private void resolveAndCheckIfFirewalled(String host) throws HTTPException {
+
+ // cached
+ if (resolvedHosts.get(host) != null) return;
+
+ // if all scripts are trustworthy (local FS), continue
+ if (Main.originAddr == null) return;
+
+ // resolve using DNS
+ try {
+ InetAddress addr = InetAddress.getByName(host);
+ byte[] quadbyte = addr.getAddress();
+ if ((quadbyte[0] == 10 ||
+ (quadbyte[0] == 192 && quadbyte[1] == 168) ||
+ (quadbyte[0] == 172 && (quadbyte[1] & 0xF0) == 16)) && !addr.equals(Main.originAddr))
+ throw new HTTPException("security violation: " + host + " [" + addr.getHostAddress() +
+ "] is in a firewalled netblock");
+ return;
+ } catch (UnknownHostException uhe) { }
+
+ if (Platform.detectProxy() == null)
+ throw new HTTPException("could not resolve hostname \"" + host + "\" and no proxy configured");
+ }
+
+
+ // Methods to attempt socket creation /////////////////////////////////////////////////////////////////
+
+ private Socket getSocket(String host, int port, boolean ssl, boolean negotiate) throws IOException {
+ Socket ret = ssl ? new SSL(host, port, negotiate) : new Socket(java.net.InetAddress.getByName(host), port);
+ ret.setTcpNoDelay(true);
+ return ret;
+ }
+
+ /** Attempts a direct connection */
+ private Socket attemptDirect() {
+ try {
+ Log.info(this, "attempting to create unproxied socket to " +
+ host + ":" + port + (ssl ? " [ssl]" : ""));
+ return getSocket(host, port, ssl, true);
+ } catch (IOException e) {
+ if (Log.on) Log.info(this, "exception in attemptDirect(): " + e);
+ return null;
+ }
+ }
+
+ /** Attempts to use an HTTP proxy, employing the CONNECT method if HTTPS is requested */
+ private Socket attemptHttpProxy(String proxyHost, int proxyPort) {
+ try {
+ if (Log.verbose) Log.info(this, "attempting to create HTTP proxied socket using proxy " + proxyHost + ":" + proxyPort);
+ Socket sock = getSocket(proxyHost, proxyPort, ssl, false);
+
+ if (!ssl) {
+ if (!path.startsWith("http://")) path = "http://" + host + ":" + port + path;
+ return sock;
+ }
+
+ PrintWriter pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream()));
+ BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream()));
+ pw.print("CONNECT " + host + ":" + port + " HTTP/1.1\r\n\r\n");
+ pw.flush();
+ String s = br.readLine();
+ if (s.charAt(9) != '2') throw new HTTPException("proxy refused CONNECT method: \"" + s + "\"");
+ while (br.readLine().length() > 0) { };
+ ((SSL)sock).negotiate();
+ return sock;
+
+ } catch (IOException e) {
+ if (Log.on) Log.info(this, "exception in attemptHttpProxy(): " + e);
+ return null;
+ }
+ }
+
+ /**
+ * Implements SOCKSv4 with v4a DNS extension
+ * @see http://www.socks.nec.com/protocol/socks4.protocol
+ * @see http://www.socks.nec.com/protocol/socks4a.protocol
+ */
+ private Socket attemptSocksProxy(String proxyHost, int proxyPort) {
+
+ // even if host is already a "x.y.z.w" string, we use this to parse it into bytes
+ InetAddress addr = null;
+ try { addr = InetAddress.getByName(host); } catch (Exception e) { }
+
+ if (Log.verbose) Log.info(this, "attempting to create SOCKSv4" + (addr == null ? "" : "a") +
+ " proxied socket using proxy " + proxyHost + ":" + proxyPort);
+
+ try {
+ Socket sock = getSocket(proxyHost, proxyPort, ssl, false);
+
+ DataOutputStream dos = new DataOutputStream(sock.getOutputStream());
+ dos.writeByte(0x04); // SOCKSv4(a)
+ dos.writeByte(0x01); // CONNECT
+ dos.writeShort(port & 0xffff); // port
+ if (addr == null) dos.writeInt(0x00000001); // bogus IP
+ else dos.write(addr.getAddress()); // actual IP
+ dos.writeByte(0x00); // no userid
+ if (addr == null) {
+ PrintWriter pw = new PrintWriter(new OutputStreamWriter(dos));
+ pw.print(host);
+ pw.flush();
+ dos.writeByte(0x00); // hostname null terminator
+ }
+ dos.flush();
+
+ DataInputStream dis = new DataInputStream(sock.getInputStream());
+ dis.readByte(); // reply version
+ byte success = dis.readByte(); // success/fail
+ dis.skip(6); // ip/port
+
+ if ((int)(success & 0xff) == 90) {
+ if (ssl) ((SSL)sock).negotiate();
+ return sock;
+ }
+ if (Log.on) Log.info(this, "SOCKS server denied access, code " + (success & 0xff));
+ return null;
+
+ } catch (IOException e) {
+ if (Log.on) Log.info(this, "exception in attemptSocksProxy(): " + e);
+ return null;
+ }
+ }
+
+ /** executes the PAC script and dispatches a call to one of the other attempt methods based on the result */
+ private Socket attemptPAC(org.ibex.js.JS pacFunc) {
+ if (Log.verbose) Log.info(this, "evaluating PAC script");
+ String pac = null;
+ try {
+ Object obj = pacFunc.call(url, host, null, null, 2);
+ if (Log.verbose) Log.info(this, " PAC script returned \"" + obj + "\"");
+ pac = obj.toString();
+ } catch (Throwable e) {
+ if (Log.on) Log.info(this, "PAC script threw exception " + e);
+ return null;
+ }
+
+ StringTokenizer st = new StringTokenizer(pac, ";", false);
+ while (st.hasMoreTokens()) {
+ String token = st.nextToken().trim();
+ if (Log.verbose) Log.info(this, " trying \"" + token + "\"...");
+ try {
+ Socket ret = null;
+ if (token.startsWith("DIRECT"))
+ ret = attemptDirect();
+ else if (token.startsWith("PROXY"))
+ ret = attemptHttpProxy(token.substring(token.indexOf(' ') + 1, token.indexOf(':')),
+ Integer.parseInt(token.substring(token.indexOf(':') + 1)));
+ else if (token.startsWith("SOCKS"))
+ ret = attemptSocksProxy(token.substring(token.indexOf(' ') + 1, token.indexOf(':')),
+ Integer.parseInt(token.substring(token.indexOf(':') + 1)));
+ if (ret != null) return ret;
+ } catch (Throwable e) {
+ if (Log.on) Log.info(this, "attempt at \"" + token + "\" failed due to " + e + "; trying next token");
+ }
+ }
+ if (Log.on) Log.info(this, "all PAC results exhausted");
+ return null;
+ }
+
+
+ // Everything Else ////////////////////////////////////////////////////////////////////////////
+
+ private synchronized void connect() throws IOException {
+ if (originalUrl.equals("stdio:")) { in = new BufferedInputStream(System.in); return; }
+ if (sock != null) {
+ if (in == null) in = new BufferedInputStream(sock.getInputStream());
+ return;
+ }
+ // grab the userinfo; gcj doesn't have java.net.URL.getUserInfo()
+ String url = originalUrl;
+ userInfo = url.substring(url.indexOf("://") + 3);
+ userInfo = userInfo.indexOf('/') == -1 ? userInfo : userInfo.substring(0, userInfo.indexOf('/'));
+ if (userInfo.indexOf('@') != -1) {
+ userInfo = userInfo.substring(0, userInfo.indexOf('@'));
+ url = url.substring(0, url.indexOf("://") + 3) + url.substring(url.indexOf('@') + 1);
+ } else {
+ userInfo = null;
+ }
+
+ if (url.startsWith("https:")) {
+ ssl = true;
+ } else if (!url.startsWith("http:")) {
+ throw new IOException("HTTP only supports http/https urls");
+ }
+ if (url.indexOf("://") == -1) throw new IOException("URLs must contain a ://");
+ String temphost = url.substring(url.indexOf("://") + 3);
+ path = temphost.substring(temphost.indexOf('/'));
+ temphost = temphost.substring(0, temphost.indexOf('/'));
+ if (temphost.indexOf(':') != -1) {
+ port = Integer.parseInt(temphost.substring(temphost.indexOf(':')+1));
+ temphost = temphost.substring(0, temphost.indexOf(':'));
+ } else {
+ port = ssl ? 443 : 80;
+ }
+ if (!skipResolveCheck) resolveAndCheckIfFirewalled(temphost);
+ host = temphost;
+ if (Log.verbose) Log.info(this, "creating HTTP object for connection to " + host + ":" + port);
+
+ Proxy pi = Platform.detectProxy();
+ OUTER: do {
+ if (pi != null) {
+ for(int i=0; i length) len = length;
+ int ret = b == null ? (int)super.skip(len) : super.read(b, off, len);
+ if (ret >= 0) {
+ length -= ret;
+ good = true;
+ }
+ return ret;
+ } finally {
+ if (!good) reset();
+ }
+ }
+
+ public void close() throws IOException {
+ if (contentLength == -1) {
+ while(!chunkedDone) {
+ if (length != 0) skip(length);
+ readChunk();
+ }
+ skip(2);
+ } else {
+ if (length != 0) skip(length);
+ }
+ if (releaseMe != null) releaseMe.release();
+ }
+ }
+
+ void reset() {
+ firstRequest = true;
+ in = null;
+ sock = null;
+ }
+
+
+ // Misc Helpers ///////////////////////////////////////////////////////////////////////////////////
+
+ /** reads a set of HTTP headers off of the input stream, returning null if the stream is already at its end */
+ private Hashtable parseHeaders(InputStream in) throws IOException {
+ Hashtable ret = new Hashtable();
+
+ // we can't use a BufferedReader directly on the input stream, since it will buffer past the end of the headers
+ byte[] buf = new byte[4096];
+ int buflen = 0;
+ while(true) {
+ int read = in.read();
+ if (read == -1 && buflen == 0) return null;
+ if (read == -1) throw new HTTPException("stream closed while reading headers");
+ buf[buflen++] = (byte)read;
+ if (buflen >= 4 && buf[buflen - 4] == '\r' && buf[buflen - 3] == '\n' &&
+ buf[buflen - 2] == '\r' && buf[buflen - 1] == '\n')
+ break;
+ if (buflen >=2 && buf[buflen - 1] == '\n' && buf[buflen - 2] == '\n')
+ break; // nice for people using stdio
+ if (buflen == buf.length) {
+ byte[] newbuf = new byte[buf.length * 2];
+ System.arraycopy(buf, 0, newbuf, 0, buflen);
+ buf = newbuf;
+ }
+ }
+
+ BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, buflen)));
+ String s = br.readLine();
+ if (!s.startsWith("HTTP/")) throw new HTTPException("Expected reply to start with \"HTTP/\", got: " + s);
+ ret.put("STATUSLINE", s.substring(s.indexOf(' ') + 1));
+ ret.put("HTTP", s.substring(5, s.indexOf(' ')));
+
+ while((s = br.readLine()) != null && s.length() > 0) {
+ String front = s.substring(0, s.indexOf(':')).toLowerCase();
+ String back = s.substring(s.indexOf(':') + 1).trim();
+ // ugly hack: we never replace a Digest-auth with a Basic-auth (proxy + www)
+ if (front.endsWith("-authenticate") && ret.get(front) != null && !back.equals("Digest")) continue;
+ ret.put(front, back);
+ }
+ return ret;
+ }
+
+ private Hashtable parseAuthenticationChallenge(String s) {
+ Hashtable ret = new Hashtable();
+
+ s = s.trim();
+ ret.put("AUTHTYPE", s.substring(0, s.indexOf(' ')));
+ s = s.substring(s.indexOf(' ')).trim();
+
+ while (s.length() > 0) {
+ String val = null;
+ String key = s.substring(0, s.indexOf('='));
+ s = s.substring(s.indexOf('=') + 1);
+ if (s.charAt(0) == '\"') {
+ s = s.substring(1);
+ val = s.substring(0, s.indexOf('\"'));
+ s = s.substring(s.indexOf('\"') + 1);
+ } else {
+ val = s.indexOf(',') == -1 ? s : s.substring(0, s.indexOf(','));
+ s = s.indexOf(',') == -1 ? "" : s.substring(s.indexOf(',') + 1);
+ }
+ if (s.length() > 0 && s.charAt(0) == ',') s = s.substring(1);
+ s = s.trim();
+ ret.put(key, val);
+ }
+ return ret;
+ }
+
+ private String H(String s) throws IOException {
+ byte[] b = s.getBytes("UTF8");
+ MD5 md5 = new MD5();
+ md5.update(b, 0, b.length);
+ byte[] out = new byte[md5.getDigestSize()];
+ md5.doFinal(out, 0);
+ String ret = "";
+ for(int i=0; i> 4);
+ ret += "0123456789abcdef".charAt(out[i] & 0x0f);
+ }
+ return ret;
+ }
+
+
+ // Proxy ///////////////////////////////////////////////////////////
+
+ /** encapsulates most of the proxy logic; some is shared in HTTP.java */
+ public static class Proxy {
+
+ public String httpProxyHost = null; ///< the HTTP Proxy host to use
+ public int httpProxyPort = -1; ///< the HTTP Proxy port to use
+ public String httpsProxyHost = null; ///< seperate proxy for HTTPS
+ public int httpsProxyPort = -1;
+ public String socksProxyHost = null; ///< the SOCKS Proxy Host to use
+ public int socksProxyPort = -1; ///< the SOCKS Proxy Port to use
+ public String[] excluded = new String[] { }; ///< hosts to be excluded from proxy use; wildcards permitted
+ public JS proxyAutoConfigFunction = null; ///< the PAC script
+
+ public static Proxy detectProxyViaManual() {
+ Proxy ret = new Proxy();
+
+ ret.httpProxyHost = Platform.getEnv("http_proxy");
+ if (ret.httpProxyHost != null) {
+ if (ret.httpProxyHost.startsWith("http://")) ret.httpProxyHost = ret.httpProxyHost.substring(7);
+ if (ret.httpProxyHost.endsWith("/"))
+ ret.httpProxyHost = ret.httpProxyHost.substring(0, ret.httpProxyHost.length() - 1);
+ if (ret.httpProxyHost.indexOf(':') != -1) {
+ ret.httpProxyPort = Integer.parseInt(ret.httpProxyHost.substring(ret.httpProxyHost.indexOf(':') + 1));
+ ret.httpProxyHost = ret.httpProxyHost.substring(0, ret.httpProxyHost.indexOf(':'));
+ } else {
+ ret.httpProxyPort = 80;
+ }
+ }
+
+ ret.httpsProxyHost = Platform.getEnv("https_proxy");
+ if (ret.httpsProxyHost != null) {
+ if (ret.httpsProxyHost.startsWith("https://")) ret.httpsProxyHost = ret.httpsProxyHost.substring(7);
+ if (ret.httpsProxyHost.endsWith("/"))
+ ret.httpsProxyHost = ret.httpsProxyHost.substring(0, ret.httpsProxyHost.length() - 1);
+ if (ret.httpsProxyHost.indexOf(':') != -1) {
+ ret.httpsProxyPort = Integer.parseInt(ret.httpsProxyHost.substring(ret.httpsProxyHost.indexOf(':') + 1));
+ ret.httpsProxyHost = ret.httpsProxyHost.substring(0, ret.httpsProxyHost.indexOf(':'));
+ } else {
+ ret.httpsProxyPort = 80;
+ }
+ }
+
+ ret.socksProxyHost = Platform.getEnv("socks_proxy");
+ if (ret.socksProxyHost != null) {
+ if (ret.socksProxyHost.startsWith("socks://")) ret.socksProxyHost = ret.socksProxyHost.substring(7);
+ if (ret.socksProxyHost.endsWith("/"))
+ ret.socksProxyHost = ret.socksProxyHost.substring(0, ret.socksProxyHost.length() - 1);
+ if (ret.socksProxyHost.indexOf(':') != -1) {
+ ret.socksProxyPort = Integer.parseInt(ret.socksProxyHost.substring(ret.socksProxyHost.indexOf(':') + 1));
+ ret.socksProxyHost = ret.socksProxyHost.substring(0, ret.socksProxyHost.indexOf(':'));
+ } else {
+ ret.socksProxyPort = 80;
+ }
+ }
+
+ String noproxy = Platform.getEnv("no_proxy");
+ if (noproxy != null) {
+ StringTokenizer st = new StringTokenizer(noproxy, ",");
+ ret.excluded = new String[st.countTokens()];
+ for(int i=0; st.hasMoreTokens(); i++) ret.excluded[i] = st.nextToken();
+ }
+
+ if (ret.httpProxyHost == null && ret.socksProxyHost == null) return null;
+ return ret;
+ }
+
+ public static JSScope proxyAutoConfigRootScope = new ProxyAutoConfigRootScope();
+ public static JS getProxyAutoConfigFunction(String url) {
+ try {
+ BufferedReader br = new BufferedReader(new InputStreamReader(new HTTP(url, true).GET()));
+ String s = null;
+ String script = "";
+ while((s = br.readLine()) != null) script += s + "\n";
+ if (Log.on) Log.info(Proxy.class, "successfully retrieved WPAD PAC:");
+ if (Log.on) Log.info(Proxy.class, script);
+
+ // MS CARP hack
+ Vector carpHosts = new Vector();
+ for(int i=0; i= d1 && day <= d2) || (d1 > d2 && (day >= d1 || day <= d2))) ? T : F;
+
+ case "dateRange": throw new JSExn("Ibex does not support dateRange() in PAC scripts");
+ case "timeRange": throw new JSExn("Ibex does not support timeRange() in PAC scripts");
+ //#end
+ return super.callMethod(method, a0, a1, a2, rest, nargs);
+ }
+ private static boolean match(String[] arr, String s, int index) {
+ if (index >= arr.length) return true;
+ for(int i=0; i epoch.
+ time *= 10000; // tenths of a microsecond.
+ // convert to little-endian byte array.
+ byte[] timestamp = new byte[8];
+ for (int i = 0; i < 8; i++) {
+ timestamp[i] = (byte) time;
+ time >>>= 8;
+ }
+ byte[] blob = new byte[blobSignature.length + reserved.length +
+ timestamp.length + clientChallenge.length +
+ unknown1.length + targetInformation.length +
+ unknown2.length];
+ int offset = 0;
+ System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length);
+ offset += blobSignature.length;
+ System.arraycopy(reserved, 0, blob, offset, reserved.length);
+ offset += reserved.length;
+ System.arraycopy(timestamp, 0, blob, offset, timestamp.length);
+ offset += timestamp.length;
+ System.arraycopy(clientChallenge, 0, blob, offset,
+ clientChallenge.length);
+ offset += clientChallenge.length;
+ System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
+ offset += unknown1.length;
+ System.arraycopy(targetInformation, 0, blob, offset,
+ targetInformation.length);
+ offset += targetInformation.length;
+ System.arraycopy(unknown2, 0, blob, offset, unknown2.length);
+ return blob;
+ }
+
+ /**
+ * Calculates the HMAC-MD5 hash of the given data using the specified
+ * hashing key.
+ *
+ * @param data The data for which the hash will be calculated.
+ * @param key The hashing key.
+ *
+ * @return The HMAC-MD5 hash of the given data.
+ */
+ private static byte[] hmacMD5(byte[] data, byte[] key) {
+ byte[] ipad = new byte[64];
+ byte[] opad = new byte[64];
+ for (int i = 0; i < 64; i++) {
+ ipad[i] = (byte) 0x36;
+ opad[i] = (byte) 0x5c;
+ }
+ for (int i = key.length - 1; i >= 0; i--) {
+ ipad[i] ^= key[i];
+ opad[i] ^= key[i];
+ }
+ byte[] content = new byte[data.length + 64];
+ System.arraycopy(ipad, 0, content, 0, 64);
+ System.arraycopy(data, 0, content, 64, data.length);
+ MD5 md5 = new MD5();
+ md5.update(content, 0, content.length);
+ data = new byte[md5.getDigestSize()];
+ md5.doFinal(data, 0);
+ content = new byte[data.length + 64];
+ System.arraycopy(opad, 0, content, 0, 64);
+ System.arraycopy(data, 0, content, 64, data.length);
+ md5 = new MD5();
+ md5.update(content, 0, content.length);
+ byte[] ret = new byte[md5.getDigestSize()];
+ md5.doFinal(ret, 0);
+ return ret;
+ }
+
+ /**
+ * Creates a DES encryption key from the given key material.
+ *
+ * @param bytes A byte array containing the DES key material.
+ * @param offset The offset in the given byte array at which
+ * the 7-byte key material starts.
+ *
+ * @return A DES encryption key created from the key material
+ * starting at the specified offset in the given byte array.
+ */
+ /*
+ private static Key createDESKey(byte[] bytes, int offset) {
+ byte[] keyBytes = new byte[7];
+ System.arraycopy(bytes, offset, keyBytes, 0, 7);
+ byte[] material = new byte[8];
+ material[0] = keyBytes[0];
+ material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1);
+ material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2);
+ material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3);
+ material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4);
+ material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5);
+ material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6);
+ material[7] = (byte) (keyBytes[6] << 1);
+ oddParity(material);
+ return new SecretKeySpec(material, "DES");
+ }
+ */
+
+ /**
+ * Applies odd parity to the given byte array.
+ *
+ * @param bytes The data whose parity bits are to be adjusted for
+ * odd parity.
+ */
+ private static void oddParity(byte[] bytes) {
+ for (int i = 0; i < bytes.length; i++) {
+ byte b = bytes[i];
+ boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^
+ (b >>> 4) ^ (b >>> 3) ^ (b >>> 2) ^
+ (b >>> 1)) & 0x01) == 0;
+ if (needsParity) {
+ bytes[i] |= (byte) 0x01;
+ } else {
+ bytes[i] &= (byte) 0xfe;
+ }
+ }
+ }
+
+ }
+ }
+}
diff --git a/upstream/org.ibex.core/src/org/ibex/net/SOAP.java b/upstream/org.ibex.core/src/org/ibex/net/SOAP.java
new file mode 100644
index 0000000..85670a3
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/net/SOAP.java
@@ -0,0 +1,282 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.net;
+
+import java.io.*;
+import java.util.*;
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.crypto.*;
+
+/**
+ * A partial RPC-style SOAP 1.1 client. Implemented from the SOAP 1.1
+ * Spec and Dave Winer's "SOAP for Busy Developers". This class
+ * extends XMLRPC in order to share some networking logic.
+ *
+ * Currently unsupported features/hacks:
+ *
Multi-ref data and circular references
+ *
'Document Style'
+ *
WSDL support
+ *
+ */
+public class SOAP extends XMLRPC {
+
+ /** the desired content of the SOAPAction header */
+ String action = null;
+
+ /** the namespace to use */
+ String nameSpace = null;
+
+ /** When you get a property from an SOAP, it just returns another SOAP with the property name tacked onto methodname. */
+ public Object get(Object name) {
+ return new SOAP(url, (method.equals("") ? "" : method + ".") + name.toString(), this, action, nameSpace); }
+
+
+ // Methods to Recieve and parse SOAP Responses ////////////////////////////////////////////////////
+
+ public void startElement(String name, String[] keys, Object[] vals, int line, int col) {
+
+ content.reset();
+ if (name.equals("SOAP-ENV:Envelope")) return;
+ if (name.equals("SOAP-ENV:Body")) return;
+ if (name.equals("SOAP-ENV:Fault")) fault = true;
+
+ // add a generic struct; we'll change this if our type is different
+ objects.addElement(new JS());
+
+ for(int i=0; i 0 && content.toString().trim().length() > 0) {
+
+ // remove ourselves
+ Object me = objects.elementAt(objects.size() - 1);
+
+ if (fault || me instanceof String) {
+ objects.removeElementAt(objects.size() - 1);
+ objects.addElement(new String(content.getBuf(), 0, content.size()).intern());
+ content.reset();
+
+ } else if (me instanceof byte[]) {
+ objects.removeElementAt(objects.size() - 1);
+ objects.addElement(new Stream.ByteArray(Base64.decode(new String(content.getBuf(), 0, content.size())), null));
+ content.reset();
+
+ } else if (me instanceof Integer) {
+ objects.removeElementAt(objects.size() - 1);
+ objects.addElement(new Integer(new String(content.getBuf(), 0, content.size())));
+ content.reset();
+
+ } else if (me instanceof Boolean) {
+ objects.removeElementAt(objects.size() - 1);
+ String s = new String(content.getBuf(), 0, content.size()).trim();
+ if (s.equals("1") || s.equals("true")) objects.addElement(Boolean.TRUE);
+ else objects.addElement(Boolean.FALSE);
+ content.reset();
+
+ } else if (me instanceof Double) {
+ objects.removeElementAt(objects.size() - 1);
+ objects.addElement(new Double(new String(content.getBuf(), 0, content.size())));
+ content.reset();
+
+ } else {
+ // okay, we got PCDATA for what is supposedly a
+ // struct... somebody's not adding their type info...
+ String s = new String(content.getBuf(), 0, content.size()).trim();
+ boolean hasdot = false;
+ for(int i=0; i 1 ? objects.elementAt(objects.size() - 2) : null;
+
+ // we want to fold stuff back into the fault object
+ if (objects.size() < 2) return;
+
+ // our parent "should" be an aggregate type -- add ourselves to it.
+ if (parent != null && parent instanceof JSArray) {
+ objects.removeElementAt(objects.size() - 1);
+ ((JSArray)parent).addElement(me);
+
+ } else if (parent != null && parent instanceof JS) {
+ objects.removeElementAt(objects.size() - 1);
+ try {
+ ((JS)parent).put(name, me);
+ } catch (JSExn e) {
+ throw new Error("this should never happen");
+ }
+
+ }
+
+ }
+
+ /** Appends the SOAP representation of o to sb */
+ void appendObject(String name, Object o, StringBuffer sb) throws JSExn {
+ if (o instanceof Number) {
+ if ((double)((Number)o).intValue() == ((Number)o).doubleValue()) {
+ sb.append(" <" + name + " xsi:type=\"xsd:int\">");
+ sb.append(((Number)o).intValue());
+ sb.append("" + name + ">\r\n");
+ } else {
+ sb.append(" <" + name + " xsi:type=\"xsd:double\">");
+ sb.append(o);
+ sb.append("" + name + ">\r\n");
+ }
+
+ } else if (o instanceof Boolean) {
+ sb.append(" <" + name + " xsi:type=\"xsd:boolean\">");
+ sb.append(((Boolean)o).booleanValue() ? "true" : "false");
+ sb.append("" + name + ">\r\n");
+
+ } else if (o instanceof Stream) {
+ try {
+ sb.append(" <" + name + " xsi:type=\"SOAP-ENC:base64\">\r\n");
+ InputStream is = ((Stream)o).getInputStream();
+ byte[] buf = new byte[54];
+ while(true) {
+ int numread = is.read(buf, 0, 54);
+ if (numread == -1) break;
+ byte[] writebuf = buf;
+ if (numread < buf.length) {
+ writebuf = new byte[numread];
+ System.arraycopy(buf, 0, writebuf, 0, numread);
+ }
+ sb.append(" ");
+ sb.append(new String(Base64.encode(writebuf)));
+ sb.append("\r\n");
+ }
+ sb.append(((Boolean)o).booleanValue() ? "1" : "0");
+ sb.append("" + name + ">\r\n");
+ } catch (IOException e) {
+ if (Log.on) Log.info(this, "caught IOException while attempting to send a ByteStream via SOAP");
+ if (Log.on) Log.info(this, e);
+ throw new JSExn("caught IOException while attempting to send a ByteStream via SOAP");
+ }
+
+ } else if (o instanceof String) {
+ sb.append(" <" + name + " xsi:type=\"xsd:string\">");
+ String s = (String)o;
+ if (s.indexOf('<') == -1 && s.indexOf('&') == -1) {
+ sb.append(s);
+ } else {
+ char[] cbuf = s.toCharArray();
+ while(true) {
+ int oldi = 0, i=0;
+ while(i < cbuf.length && cbuf[i] != '<' && cbuf[i] != '&') i++;
+ sb.append(cbuf, oldi, i);
+ if (i == cbuf.length) break;
+ if (cbuf[i] == '<') sb.append("<");
+ else if (cbuf[i] == '&') sb.append("&");
+ i = oldi = i + 1;
+ }
+ }
+ sb.append("" + name + ">\r\n");
+
+ } else if (o instanceof JSArray) {
+ JSArray a = (JSArray)o;
+ sb.append(" <" + name + " SOAP-ENC:arrayType=\"xsd:ur-type[" + a.length() + "]\">");
+ for(int i=0; i\r\n");
+
+ } else if (o instanceof JS) {
+ JS j = (JS)o;
+ sb.append(" <" + name + ">");
+ Enumeration e = j.keys();
+ while(e.hasMoreElements()) {
+ Object key = e.nextElement();
+ appendObject((String)key, j.get(key), sb);
+ }
+ sb.append("" + name + ">\r\n");
+
+ }
+ }
+
+ protected String buildRequest(JSArray args) throws JSExn, IOException {
+ // build up the request
+ StringBuffer content = new StringBuffer();
+ content.append("SOAPAction: " + action + "\r\n\r\n");
+ content.append("\r\n");
+ content.append("\r\n");
+ content.append("\r\n");
+ content.append(" <");
+ content.append(method);
+ content.append(nameSpace != null ? " xmlns=\"" + nameSpace + "\"" : "");
+ content.append(">\r\n");
+ if (args.length() > 0) {
+ Enumeration e = ((JS)args.elementAt(0)).keys();
+ while(e.hasMoreElements()) {
+ Object key = e.nextElement();
+ appendObject((String)key, ((JS)args.elementAt(0)).get(key), content);
+ }
+ }
+ content.append(" " + method + ">\r\n");
+ return content.toString();
+ }
+
+ public SOAP(String url, String methodname, String action, String nameSpace) {
+ super(url, methodname);
+ this.action = action;
+ this.nameSpace = nameSpace;
+ }
+ public SOAP(String url, String methodname, SOAP httpSource, String action, String nameSpace) {
+ super(url, methodname, httpSource);
+ this.action = action;
+ this.nameSpace = nameSpace;
+ }
+
+}
diff --git a/upstream/org.ibex.core/src/org/ibex/net/XMLRPC.java b/upstream/org.ibex.core/src/org/ibex/net/XMLRPC.java
new file mode 100644
index 0000000..14aacb5
--- /dev/null
+++ b/upstream/org.ibex.core/src/org/ibex/net/XMLRPC.java
@@ -0,0 +1,348 @@
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.net;
+
+import java.io.*;
+import java.util.*;
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.crypto.*;
+
+/**
+ * An XML-RPC client implemented as a JavaScript Host Object. See the
+ * Ibex spec for information on its behavior.
+ *
+ * NOTE: this client is EXTREMELY lenient in the responses it will
+ * accept; there are many, many invalid responses that it will
+ * successfully parse and return. Do NOT use this to determine the
+ * validity of your server.
+ *
+ * This client conforms to The
+ * XML-RPC Spec, subject to these limitations:
+ *
+ *
XMLRPC cannot invoke methods that require a argument
+ *
if a return value contains a , it will be returned as a string
+ *
The decision to pass a number as or is based
+ * entirely on whether or not the argument is fractional. Thus, it
+ * is impossible to pass a non-fractional number to an xmlrpc
+ * method that insists on being called with a element. We
+ * hope that most xml-rpc servers will be able to automatically
+ * convert.
+ *