2002/03/21 01:19:32
authormegacz <megacz@xwt.org>
Fri, 30 Jan 2004 06:45:03 +0000 (06:45 +0000)
committermegacz <megacz@xwt.org>
Fri, 30 Jan 2004 06:45:03 +0000 (06:45 +0000)
darcs-hash:20040130064503-2ba56-caff6813a1e43e4303cb1a078470e2b10739b027.gz

97 files changed:
src/org/bouncycastle/asn1/BERConstructedOctetString.java [new file with mode: 0644]
src/org/bouncycastle/asn1/BERConstructedSequence.java [new file with mode: 0644]
src/org/bouncycastle/asn1/BERInputStream.java [new file with mode: 0644]
src/org/bouncycastle/asn1/BEROutputStream.java [new file with mode: 0644]
src/org/bouncycastle/asn1/BERTaggedObject.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERBMPString.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERBitString.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERBoolean.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERConstructedSequence.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERConstructedSet.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DEREncodable.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERIA5String.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERInputStream.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERInteger.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERObject.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERObjectIdentifier.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DEROctetString.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DEROutputStream.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERPrintableString.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERSet.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERString.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERT61String.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERTaggedObject.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERTags.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERUTCTime.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERUnknownTag.java [new file with mode: 0644]
src/org/bouncycastle/asn1/DERVisibleString.java [new file with mode: 0644]
src/org/bouncycastle/asn1/OIDTokenizer.java [new file with mode: 0644]
src/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/BasicConstraints.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/CRLDistPoint.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/CRLNumber.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/CertificateList.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/DSAParameter.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/DigestInfo.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/DistributionPoint.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/DistributionPointName.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/GeneralName.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/GeneralNames.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/KeyUsage.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/RSAPublicKeyStructure.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/ReasonFlags.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/TBSCertList.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/TBSCertificateStructure.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/V2TBSCertListGenerator.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/X509CertificateStructure.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/X509Extension.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/X509Extensions.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/X509Name.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/X509NameTokenizer.java [new file with mode: 0644]
src/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java [new file with mode: 0644]
src/org/bouncycastle/crypto/AsymmetricBlockCipher.java [new file with mode: 0644]
src/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java [new file with mode: 0644]
src/org/bouncycastle/crypto/AsymmetricCipherKeyPairGenerator.java [new file with mode: 0644]
src/org/bouncycastle/crypto/BlockCipher.java [new file with mode: 0644]
src/org/bouncycastle/crypto/CipherKeyGenerator.java [new file with mode: 0644]
src/org/bouncycastle/crypto/CipherParameters.java [new file with mode: 0644]
src/org/bouncycastle/crypto/CryptoException.java [new file with mode: 0644]
src/org/bouncycastle/crypto/DataLengthException.java [new file with mode: 0644]
src/org/bouncycastle/crypto/Digest.java [new file with mode: 0644]
src/org/bouncycastle/crypto/InvalidCipherTextException.java [new file with mode: 0644]
src/org/bouncycastle/crypto/KeyGenerationParameters.java [new file with mode: 0644]
src/org/bouncycastle/crypto/RuntimeCryptoException.java [new file with mode: 0644]
src/org/bouncycastle/crypto/StreamBlockCipher.java [new file with mode: 0644]
src/org/bouncycastle/crypto/StreamCipher.java [new file with mode: 0644]
src/org/bouncycastle/crypto/digests/GeneralDigest.java [new file with mode: 0644]
src/org/bouncycastle/crypto/digests/MD2Digest.java [new file with mode: 0644]
src/org/bouncycastle/crypto/digests/MD5Digest.java [new file with mode: 0644]
src/org/bouncycastle/crypto/digests/SHA1Digest.java [new file with mode: 0644]
src/org/bouncycastle/crypto/encodings/PKCS1Encoding.java [new file with mode: 0644]
src/org/bouncycastle/crypto/engines/RC4Engine.java [new file with mode: 0644]
src/org/bouncycastle/crypto/engines/RSAEngine.java [new file with mode: 0644]
src/org/bouncycastle/crypto/params/AsymmetricKeyParameter.java [new file with mode: 0644]
src/org/bouncycastle/crypto/params/KeyParameter.java [new file with mode: 0644]
src/org/bouncycastle/crypto/params/ParametersWithRandom.java [new file with mode: 0644]
src/org/bouncycastle/crypto/params/RSAKeyParameters.java [new file with mode: 0644]
src/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java [new file with mode: 0644]
src/org/bouncycastle/util/encoders/Base64.java [new file with mode: 0644]
src/org/mozilla/javascript/Arguments.java [new file with mode: 0644]
src/org/mozilla/javascript/BaseFunction.java [new file with mode: 0644]
src/org/mozilla/javascript/BinaryDigitReader.java [new file with mode: 0644]
src/org/mozilla/javascript/ClassDefinitionException.java [new file with mode: 0644]
src/org/mozilla/javascript/ClassNameHelper.java [new file with mode: 0644]
src/org/mozilla/javascript/ClassOutput.java [new file with mode: 0644]
src/org/mozilla/javascript/Context.java [new file with mode: 0644]
src/org/mozilla/javascript/ContextListener.java [new file with mode: 0644]
src/org/mozilla/javascript/DToA.java [new file with mode: 0644]
src/org/mozilla/javascript/DebuggableEngineImpl.java [new file with mode: 0644]
src/org/mozilla/javascript/DefaultErrorReporter.java [new file with mode: 0644]
src/org/mozilla/javascript/Delegator.java [new file with mode: 0644]
src/org/mozilla/javascript/EcmaError.java [new file with mode: 0644]

diff --git a/src/org/bouncycastle/asn1/BERConstructedOctetString.java b/src/org/bouncycastle/asn1/BERConstructedOctetString.java
new file mode 100644 (file)
index 0000000..47b4245
--- /dev/null
@@ -0,0 +1,145 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+import java.util.*;
+
+public class BERConstructedOctetString
+    extends DEROctetString
+{
+    /**
+     * convert a vector of octet strings into a single byte string
+     */
+    static private byte[] toBytes(
+        Vector  octs)
+    {
+        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
+
+        for (int i = 0; i != octs.size(); i++)
+        {
+            DEROctetString  o = (DEROctetString)octs.elementAt(i);
+
+            try
+            {
+                bOut.write(o.getOctets());
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException("exception converting octets " + e.toString());
+            }
+        }
+
+        return bOut.toByteArray();
+    }
+
+    private Vector  octs;
+
+    /**
+     * @param string the octets making up the octet string.
+     */
+    public BERConstructedOctetString(
+        byte[]  string)
+    {
+               super(string);
+    }
+
+    public BERConstructedOctetString(
+        Vector  octs)
+    {
+               super(toBytes(octs));
+
+        this.octs = octs;
+    }
+
+    public BERConstructedOctetString(
+        DERObject  obj)
+    {
+               super(obj);
+    }
+
+    public BERConstructedOctetString(
+        DEREncodable  obj)
+    {
+        super(obj.getDERObject());
+    }
+
+    public byte[] getOctets()
+    {
+        return string;
+    }
+
+    public Vector getDEROctets()
+    {
+        if (octs == null)
+        {
+            octs = generateOcts();
+        }
+
+        return octs;
+    }
+
+    private Vector generateOcts()
+    {
+        int     start = 0;
+        int     end = 0;
+        Vector  vec = new Vector();
+
+        while (end < string.length)
+        {
+            if ((end + 1) < string.length)
+            {
+                if (string[end] == 0 && string[end + 1] == 0)
+                {
+                    byte[]  nStr = new byte[end - start + 1];
+
+                    for (int i = 0; i != nStr.length; i++)
+                    {
+                        nStr[i] = string[start + i];
+                    }
+
+                    vec.addElement(new DEROctetString(nStr));
+                    start = end + 1;
+                }
+            }
+            end++;
+        }
+
+        byte[]  nStr = new byte[end - start];
+        for (int i = 0; i != nStr.length; i++)
+        {
+            nStr[i] = string[start + i];
+        }
+
+        vec.addElement(new DEROctetString(nStr));
+
+        return vec;
+    }
+
+    public void encode(
+        DEROutputStream out)
+        throws IOException
+    {
+               if (out instanceof BEROutputStream)
+               {
+            out.write(CONSTRUCTED | OCTET_STRING);
+
+            out.write(0x80);
+
+            if (octs == null)
+            {
+                octs = generateOcts();
+            }
+
+            for (int i = 0; i != octs.size(); i++)
+            {
+                out.writeObject(octs.elementAt(i));
+            }
+
+            out.write(0x00);
+            out.write(0x00);
+               }
+               else
+               {
+                       super.encode(out);
+               }
+    }
+}
diff --git a/src/org/bouncycastle/asn1/BERConstructedSequence.java b/src/org/bouncycastle/asn1/BERConstructedSequence.java
new file mode 100644 (file)
index 0000000..b8a3c2d
--- /dev/null
@@ -0,0 +1,49 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+import java.util.*;
+
+public class BERConstructedSequence
+    extends DERConstructedSequence
+{
+    /*
+     * A note on the implementation:
+     * <p>
+     * As DER requires the constructed, definite-length model to
+     * be used for structured types, this varies slightly from the
+     * ASN.1 descriptions given. Rather than just outputing SEQUENCE,
+     * we also have to specify CONSTRUCTED, and the objects length.
+     */
+    void encode(
+        DEROutputStream out)
+        throws IOException
+    {
+        if (out instanceof BEROutputStream)
+        {
+            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
+            BEROutputStream         dOut = new BEROutputStream(bOut);
+            Enumeration             e = getObjects();
+
+            while (e.hasMoreElements())
+            {
+                Object    obj = e.nextElement();
+
+                dOut.writeObject(obj);
+            }
+
+            dOut.close();
+
+            byte[]  bytes = bOut.toByteArray();
+
+            out.write(SEQUENCE | CONSTRUCTED);
+            out.write(0x80);
+            out.write(bytes);
+            out.write(0x00);
+            out.write(0x00);
+        }
+        else
+        {
+            super.encode(out);
+        }
+    }
+}
diff --git a/src/org/bouncycastle/asn1/BERInputStream.java b/src/org/bouncycastle/asn1/BERInputStream.java
new file mode 100644 (file)
index 0000000..824b439
--- /dev/null
@@ -0,0 +1,159 @@
+package org.bouncycastle.asn1;
+
+import java.math.BigInteger;
+import java.io.*;
+import java.util.*;
+
+public class BERInputStream
+    extends DERInputStream
+{
+       private DERObject END_OF_STREAM = new DERObject() {
+                                                                               void encode(
+                                                                                       DEROutputStream out)
+                                                                               throws IOException
+                                                                               {
+                                                                                       throw new IOException("Eeek!");
+                                                                               }
+
+                                                                       };
+    public BERInputStream(
+        InputStream is)
+    {
+        super(is);
+    }
+
+    /**
+     * read a string of bytes representing an indefinite length object.
+     */
+    private byte[] readIndefiniteLengthFully()
+        throws IOException
+    {
+        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
+        int                     b, b1;
+
+        b1 = read();
+
+        while ((b = read()) >= 0)
+        {
+                       if (b1 == 0 && b == 0)
+                       {
+                               break;
+                       }
+
+            bOut.write(b1);
+            b1 = b;
+        }
+
+        return bOut.toByteArray();
+    }
+
+       private BERConstructedOctetString buildConstructedOctetString(
+               DEROctetString  o1,
+               DEROctetString  o2)
+               throws IOException
+       {
+        Vector                  octs = new Vector();
+
+               if (o1 != null)
+               {
+            octs.addElement(o1);
+            octs.addElement(o2);
+               }
+
+               for (;;)
+               {
+                       DERObject               o = readObject();
+
+                       if (o == END_OF_STREAM)
+                       {
+                               break;
+                       }
+
+            octs.addElement(o);
+               }
+
+               return new BERConstructedOctetString(octs);
+       }
+
+    public DERObject readObject()
+        throws IOException
+    {
+        int tag = read();
+        if (tag == -1)
+        {
+            throw new EOFException();
+        }
+    
+        int     length = readLength();
+
+        if (length < 0)    // indefinite length method
+        {
+            byte[]  bytes;
+    
+            switch (tag)
+            {
+            case NULL:
+                return null;
+            case SEQUENCE | CONSTRUCTED:
+                BERConstructedSequence  seq = new BERConstructedSequence();
+    
+                               for (;;)
+                               {
+                                       DERObject   obj = readObject();
+
+                                       if (obj == END_OF_STREAM)
+                                       {
+                                               break;
+                                       }
+
+                                       seq.addObject(obj);
+                               }
+                               return seq;
+            case OCTET_STRING | CONSTRUCTED:
+                               return buildConstructedOctetString(null, null);
+            default:
+                if ((tag & (TAGGED | CONSTRUCTED)) != 0)  
+                {
+                                       // with tagged object tag number is bottom 4 bits
+                    BERTaggedObject tagObj = new BERTaggedObject(tag & 0x0f, readObject());
+                                       DERObject               o = readObject();
+
+                                       if (o == END_OF_STREAM)
+                                       {
+                                               return tagObj;
+                                       }
+                                       else if (o instanceof DEROctetString
+                                                       && tagObj.getObject() instanceof DEROctetString)
+                                       {
+                                               //
+                                               // it's an implicit object - mark it as so...
+                                               //
+                                               tagObj = new BERTaggedObject(false, tag & 0x0f, 
+                                                                               buildConstructedOctetString((DEROctetString)tagObj.getObject(), (DEROctetString)o));
+
+                                               return tagObj;
+                                       }
+
+                                       throw new IOException("truncated tagged object");
+                }
+    
+               bytes = readIndefiniteLengthFully();
+
+                return buildObject(tag, bytes);
+            }
+        }
+        else
+        {
+            if (tag == 0 && length == 0)    // end of contents marker.
+            {
+                return END_OF_STREAM;
+            }
+
+            byte[]  bytes = new byte[length];
+    
+            readFully(bytes);
+    
+                       return buildObject(tag, bytes);
+        }
+    }
+}
diff --git a/src/org/bouncycastle/asn1/BEROutputStream.java b/src/org/bouncycastle/asn1/BEROutputStream.java
new file mode 100644 (file)
index 0000000..4adefed
--- /dev/null
@@ -0,0 +1,35 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+
+public class BEROutputStream
+    extends DEROutputStream
+{
+    public BEROutputStream(
+        OutputStream    os)
+    {
+        super(os);
+    }
+
+    public void writeObject(
+        Object    obj)
+        throws IOException
+    {
+        if (obj == null)
+        {
+            writeNull();
+        }
+        else if (obj instanceof DERObject)
+        {
+            ((DERObject)obj).encode(this);
+        }
+        else if (obj instanceof DEREncodable)
+        {
+            ((DEREncodable)obj).getDERObject().encode(this);
+        }
+        else
+        {
+            throw new IOException("object not BEREncodable");
+        }
+    }
+}
diff --git a/src/org/bouncycastle/asn1/BERTaggedObject.java b/src/org/bouncycastle/asn1/BERTaggedObject.java
new file mode 100644 (file)
index 0000000..b24f037
--- /dev/null
@@ -0,0 +1,97 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * BER TaggedObject - in ASN.1 nottation this is any object proceeded by
+ * a [n] where n is some number - these are assume to follow the construction
+ * rules (as with sequences).
+ */
+public class BERTaggedObject
+    extends DERTaggedObject
+{
+    /**
+     * This creates an empty tagged object of tagNo (ie. zero length).
+     *
+     * @param tagNo the tag number for this object.
+     */
+    public BERTaggedObject(
+        int     tagNo)
+    {
+               super(tagNo);
+    }
+
+    /**
+     * @param tagNo the tag number for this object.
+     * @param obj the tagged object.
+     */
+    public BERTaggedObject(
+        int         tagNo,
+        DERObject   obj)
+    {
+               super(tagNo, obj);
+    }
+
+    /**
+     * @param explicit true if an explicitly tagged object.
+     * @param tagNo the tag number for this object.
+     * @param obj the tagged object.
+     */
+    public BERTaggedObject(
+        boolean     explicit,
+        int         tagNo,
+        DERObject   obj)
+    {
+               super(explicit, tagNo, obj);
+    }
+
+    void encode(
+        DEROutputStream  out)
+        throws IOException
+    {
+               if (out instanceof BEROutputStream)
+               {
+            out.write(CONSTRUCTED | TAGGED | tagNo);
+            out.write(0x80);
+
+                       if (!empty)
+                       {
+                               ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
+                               BEROutputStream         dOut = new BEROutputStream(bOut);
+
+                if (!explicit)
+                {
+                    if (obj instanceof BERConstructedOctetString)
+                    {
+                        Vector  octs = ((BERConstructedOctetString)obj).getDEROctets();
+
+                        for (int i = 0; i != octs.size(); i++)
+                        {
+                            dOut.writeObject(octs.elementAt(i));
+                        }
+                    }
+                    else
+                    {
+                        dOut.writeObject(obj); // hmmm...
+                    }
+                }
+                else
+                {
+                                   dOut.writeObject(obj);
+                }
+
+                               dOut.close();
+
+                out.write(bOut.toByteArray());
+                       }
+
+            out.write(0x00);
+            out.write(0x00);
+               }
+               else
+               {
+                       super.encode(out);
+               }
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERBMPString.java b/src/org/bouncycastle/asn1/DERBMPString.java
new file mode 100644 (file)
index 0000000..8a58b4a
--- /dev/null
@@ -0,0 +1,59 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+
+/**
+ * DER BMPString object.
+ */
+public class DERBMPString
+    extends DERObject
+    implements DERString
+{
+    String  string;
+
+    /**
+     * basic constructor - byte encoded string.
+     */
+    public DERBMPString(
+        byte[]   string)
+    {
+           try
+           {
+            this.string = new String(string, "UnicodeBig");
+           }
+           catch (UnsupportedEncodingException e)
+           {
+            throw new RuntimeException(e.toString());
+           }
+    }
+
+    /**
+     * basic constructor
+     */
+    public DERBMPString(
+        String   string)
+    {
+        this.string = string;
+    }
+
+    public String getString()
+    {
+        return string;
+    }
+
+    void encode(
+        DEROutputStream  out)
+        throws IOException
+    {
+        char[]  c = string.toCharArray();
+        byte[]  b = new byte[c.length * 2];
+
+        for (int i = 0; i != c.length; i++)
+        {
+            b[2 * i] = (byte)((c[i] & 0xff00) >> 8);
+            b[2 * i + 1] = (byte)c[i];
+        }
+
+        out.writeEncoded(BMP_STRING, b);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERBitString.java b/src/org/bouncycastle/asn1/DERBitString.java
new file mode 100644 (file)
index 0000000..0c888d5
--- /dev/null
@@ -0,0 +1,85 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+
+public class DERBitString
+    extends DERObject
+{
+    protected byte[]      data;
+    protected int         padBits;
+
+    protected DERBitString(
+        byte    data,
+        int     padBits)
+    {
+        this.data = new byte[1];
+        this.data[0] = data;
+        this.padBits = padBits;
+    }
+
+    /**
+     * @param data the octets making up the bit string.
+     * @param padBits the number of extra bits at the end of the string.
+     */
+    public DERBitString(
+        byte[]  data,
+        int     padBits)
+    {
+        this.data = data;
+        this.padBits = padBits;
+    }
+
+    public DERBitString(
+        byte[]  data)
+    {
+        this(data, 0);
+    }
+
+    public DERBitString(
+        DERObject  obj)
+    {
+        try
+        {
+            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
+            DEROutputStream         dOut = new DEROutputStream(bOut);
+
+            dOut.writeObject(obj);
+            dOut.close();
+
+            this.data = bOut.toByteArray();
+            this.padBits = 0;
+        }
+        catch (IOException e)
+        {
+            throw new IllegalArgumentException("Error processing object : " + e.toString());
+        }
+    }
+
+    public DERBitString(
+        DEREncodable  obj)
+    {
+        this(obj.getDERObject());
+    }
+
+    public byte[] getBytes()
+    {
+        return data;
+    }
+
+    public int getPadBits()
+    {
+        return padBits;
+    }
+
+    void encode(
+        DEROutputStream  out)
+        throws IOException
+    {
+        byte[]  bytes = new byte[getBytes().length + 1];
+
+        bytes[0] = (byte)getPadBits();
+        System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1);
+
+        out.writeEncoded(BIT_STRING, bytes);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERBoolean.java b/src/org/bouncycastle/asn1/DERBoolean.java
new file mode 100644 (file)
index 0000000..4fce317
--- /dev/null
@@ -0,0 +1,37 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+
+public class DERBoolean
+    extends DERObject
+{
+    byte         value;
+
+    public DERBoolean(
+        byte[]       value)
+    {
+        this.value = value[0];
+    }
+
+    public DERBoolean(
+        boolean     value)
+    {
+        this.value = (value) ? (byte)0xff : (byte)0;
+    }
+
+    public boolean isTrue()
+    {
+        return (value != 0);
+    }
+
+    void encode(
+        DEROutputStream out)
+        throws IOException
+    {
+        byte[]  bytes = new byte[1];
+
+        bytes[0] = value;
+
+        out.writeEncoded(BOOLEAN, bytes);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERConstructedSequence.java b/src/org/bouncycastle/asn1/DERConstructedSequence.java
new file mode 100644 (file)
index 0000000..d600d95
--- /dev/null
@@ -0,0 +1,77 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+import java.util.*;
+
+public class DERConstructedSequence
+    extends DERObject
+{
+    private Vector seq = new Vector();
+
+    public DERConstructedSequence()
+    {
+    }
+
+    public void addObject(
+        DEREncodable obj)
+    {
+        seq.addElement(obj);
+    }
+
+    public Enumeration getObjects()
+    {
+        return seq.elements();
+    }
+
+    /**
+     * return the object at the sequence postion indicated by index.
+     *
+     * @param the sequence number (starting at zero) of the object
+     * @return the object at the sequence postion indicated by index.
+     */
+    public Object getObjectAt(
+        int index)
+    {
+        return seq.elementAt(index);
+    }
+
+    /**
+     * return the number of objects in this sequence.
+     *
+     * @return the number of objects in this sequence.
+     */
+    public int getSize()
+    {
+        return seq.size();
+    }
+
+    /*
+     * A note on the implementation:
+     * <p>
+     * As DER requires the constructed, definite-length model to
+     * be used for structured types, this varies slightly from the
+     * ASN.1 descriptions given. Rather than just outputing SEQUENCE,
+     * we also have to specify CONSTRUCTED, and the objects length.
+     */
+    void encode(
+        DEROutputStream out)
+        throws IOException
+    {
+        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
+        DEROutputStream         dOut = new DEROutputStream(bOut);
+        Enumeration             e = getObjects();
+
+        while (e.hasMoreElements())
+        {
+            Object    obj = e.nextElement();
+
+            dOut.writeObject(obj);
+        }
+
+        dOut.close();
+
+        byte[]  bytes = bOut.toByteArray();
+
+        out.writeEncoded(SEQUENCE | CONSTRUCTED, bytes);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERConstructedSet.java b/src/org/bouncycastle/asn1/DERConstructedSet.java
new file mode 100644 (file)
index 0000000..f675b42
--- /dev/null
@@ -0,0 +1,77 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+import java.util.*;
+
+public class DERConstructedSet
+    extends DERObject
+{
+    private Vector set = new Vector();
+
+    public DERConstructedSet()
+    {
+    }
+
+    public void addObject(
+        DEREncodable obj)
+    {
+        set.addElement(obj);
+    }
+
+    public Enumeration getObjects()
+    {
+        return set.elements();
+    }
+
+    /**
+     * return the object at the set postion indicated by index.
+     *
+     * @param the set number (starting at zero) of the object
+     * @return the object at the set postion indicated by index.
+     */
+    public Object getObjectAt(
+        int index)
+    {
+        return set.elementAt(index);
+    }
+
+    /**
+     * return the number of objects in this set.
+     *
+     * @return the number of objects in this set.
+     */
+    public int getSize()
+    {
+        return set.size();
+    }
+
+    /*
+     * A note on the implementation:
+     * <p>
+     * As DER requires the constructed, definite-length model to
+     * be used for structured types, this varies slightly from the
+     * ASN.1 descriptions given. Rather than just outputing SET,
+     * we also have to specify CONSTRUCTED, and the objects length.
+     */
+    void encode(
+        DEROutputStream out)
+        throws IOException
+    {
+        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
+        DEROutputStream         dOut = new DEROutputStream(bOut);
+        Enumeration             e = getObjects();
+
+        while (e.hasMoreElements())
+        {
+            Object    obj = e.nextElement();
+
+            dOut.writeObject(obj);
+        }
+
+        dOut.close();
+
+        byte[]  bytes = bOut.toByteArray();
+
+        out.writeEncoded(SET | CONSTRUCTED, bytes);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DEREncodable.java b/src/org/bouncycastle/asn1/DEREncodable.java
new file mode 100644 (file)
index 0000000..d89305a
--- /dev/null
@@ -0,0 +1,6 @@
+package org.bouncycastle.asn1;
+
+public interface DEREncodable
+{
+    public DERObject getDERObject();
+}
diff --git a/src/org/bouncycastle/asn1/DERIA5String.java b/src/org/bouncycastle/asn1/DERIA5String.java
new file mode 100644 (file)
index 0000000..5181bdd
--- /dev/null
@@ -0,0 +1,62 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+
+/**
+ * DER IA5String object - this is an ascii string.
+ */
+public class DERIA5String
+    extends DERObject
+    implements DERString
+{
+    String  string;
+
+    /**
+     * basic constructor - with bytes.
+     */
+    public DERIA5String(
+        byte[]   string)
+    {
+        try
+        {
+            this.string = new String(string, "US-ASCII");
+        }
+        catch(UnsupportedEncodingException e)
+        {
+            throw new RuntimeException("PANIC: " + e);
+        }
+    }
+
+    /**
+     * basic constructor - with string.
+     */
+    public DERIA5String(
+        String   string)
+    {
+        this.string = string;
+    }
+
+    public String getString()
+    {
+        return string;
+    }
+
+    public byte[] getOctets()
+    {
+        try
+        {
+            return string.getBytes("US-ASCII");
+        }
+        catch(UnsupportedEncodingException e)
+        {
+            throw new RuntimeException("PANIC: " + e);
+        }
+    }
+
+    void encode(
+        DEROutputStream  out)
+        throws IOException
+    {
+        out.writeEncoded(IA5_STRING, this.getOctets());
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERInputStream.java b/src/org/bouncycastle/asn1/DERInputStream.java
new file mode 100644 (file)
index 0000000..61de735
--- /dev/null
@@ -0,0 +1,243 @@
+package org.bouncycastle.asn1;
+
+import java.math.BigInteger;
+import java.io.*;
+
+public class DERInputStream
+    extends FilterInputStream implements DERTags
+{
+    public DERInputStream(
+        InputStream is)
+    {
+        super(is);
+    }
+
+    protected int readLength()
+        throws IOException
+    {
+        int length = read();
+        if (length < 0)
+        {
+            throw new IOException("EOF found when length expected");
+        }
+
+        if (length == 0x80)
+        {
+            return -1;      // indefinite-length encoding
+        }
+
+        if (length > 127)
+        {
+            int size = length & 0x7f;
+
+            length = 0;
+            for (int i = 0; i < size; i++)
+            {
+                int next = read();
+
+                if (next < 0)
+                {
+                    throw new IOException("EOF found reading length");
+                }
+
+                length = (length << 8) + next;
+            }
+        }
+
+        return length;
+    }
+
+    protected void readFully(
+        byte[]  bytes)
+        throws IOException
+    {
+        int     left = bytes.length;
+
+        if (left == 0)
+        {
+            return;
+        }
+
+        while ((left -= read(bytes, bytes.length - left, left)) != 0)
+        {
+            ;
+        }
+    }
+
+       /**
+        * build an object given its tag and a byte stream to construct it
+        * from.
+        */
+    protected DERObject buildObject(
+               int     tag,
+               byte[]  bytes)
+               throws IOException
+       {
+               switch (tag)
+        {
+        case NULL:
+            return null;   
+        case SEQUENCE | CONSTRUCTED:
+            ByteArrayInputStream    bIn = new ByteArrayInputStream(bytes);
+            BERInputStream          dIn = new BERInputStream(bIn);
+            DERConstructedSequence  seq = new DERConstructedSequence();
+
+            try
+            {
+                for (;;)
+                {
+                    DERObject   obj = dIn.readObject();
+
+                    seq.addObject(obj);
+                }
+            }
+            catch (EOFException ex)
+            {
+                return seq;
+            }
+        case SET | CONSTRUCTED:
+            bIn = new ByteArrayInputStream(bytes);
+            dIn = new BERInputStream(bIn);
+
+            DERSet       set = new DERSet(dIn.readObject());
+
+            try
+            {
+                for (;;)
+                {
+                    DERObject   obj = dIn.readObject();
+
+                    set.addObject(obj);
+                }
+            }
+            catch (EOFException ex)
+            {
+                return set;
+            }
+        case BOOLEAN:
+            return new DERBoolean(bytes);
+        case INTEGER:
+            return new DERInteger(bytes);
+        case OBJECT_IDENTIFIER:
+            int             head = bytes[0] & 0xff;
+            StringBuffer    objId = new StringBuffer();
+    
+            objId.append(Integer.toString(head / 40));
+            objId.append('.');
+            objId.append(Integer.toString(head % 40));
+            
+            int value = 0;
+    
+            for (int i = 1; i != bytes.length; i++)
+            {
+                int b = bytes[i] & 0xff;
+    
+                value = value * 128 + (b & 0x7f);
+                if ((b & 128) == 0)             // end of number reached
+                {
+                    objId.append('.');
+                    objId.append(Integer.toString(value));
+                    value = 0;
+                }
+            }
+    
+            return new DERObjectIdentifier(objId.toString());
+        case BIT_STRING:
+            int     padBits = bytes[0];
+            byte[]  data = new byte[bytes.length - 1];
+
+            System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
+
+            return new DERBitString(data, padBits);
+        case PRINTABLE_STRING:
+            return new DERPrintableString(bytes);
+        case IA5_STRING:
+            return new DERIA5String(bytes);
+        case T61_STRING:
+            return new DERT61String(bytes);
+        case VISIBLE_STRING:
+            return new DERVisibleString(bytes);
+        case BMP_STRING:
+            return new DERBMPString(bytes);
+        case OCTET_STRING:
+            return new DEROctetString(bytes);
+        case UTC_TIME:
+            return new DERUTCTime(new String(bytes));
+        default:
+            //
+            // with tagged object tag number is bottom 4 bits
+            //
+            if ((tag & (TAGGED | CONSTRUCTED)) != 0)  
+            {
+                if (bytes.length == 0)        // empty tag!
+                {
+                    return new DERTaggedObject(tag & 0x0f);
+                }
+
+                //
+                // simple type - implicit... return an octet string
+                //
+                if ((tag & CONSTRUCTED) == 0)
+                {
+                    return new DERTaggedObject(false, tag & 0x0f, new DEROctetString(bytes));
+                }
+
+                bIn = new ByteArrayInputStream(bytes);
+                dIn = new BERInputStream(bIn);
+
+                DEREncodable dObj = dIn.readObject();
+
+                //
+                // explicitly tagged (probably!) - if it isn't we'd have to
+                // tell from the context
+                //
+                if (dIn.available() == 0)
+                {
+                    return new DERTaggedObject(tag & 0x0f, dObj);
+                }
+
+                //
+                // another implicit object, we'll create a sequence...
+                //
+                seq = new DERConstructedSequence();
+
+                seq.addObject(dObj);
+
+                try
+                {
+                    for (;;)
+                    {
+                        dObj = dIn.readObject();
+
+                        seq.addObject(dObj);
+                    }
+                }
+                catch (EOFException ex)
+                {
+                    // ignore --
+                }
+
+                return new DERTaggedObject(false, tag & 0x0f, seq);
+            }
+
+            return new DERUnknownTag(tag, bytes);
+        }
+       }
+
+    public DERObject readObject()
+        throws IOException
+    {
+        int tag = read();
+        if (tag == -1)
+        {
+            throw new EOFException();
+        }
+
+        int     length = readLength();
+        byte[]  bytes = new byte[length];
+
+        readFully(bytes);
+
+               return buildObject(tag, bytes);
+       }
+}
diff --git a/src/org/bouncycastle/asn1/DERInteger.java b/src/org/bouncycastle/asn1/DERInteger.java
new file mode 100644 (file)
index 0000000..0f86910
--- /dev/null
@@ -0,0 +1,49 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+import java.math.BigInteger;
+
+public class DERInteger
+    extends DERObject
+{
+    byte[]      bytes;
+
+    public DERInteger(
+        int         value)
+    {
+        bytes = BigInteger.valueOf(value).toByteArray();
+    }
+
+    public DERInteger(
+        BigInteger   value)
+    {
+        bytes = value.toByteArray();
+    }
+
+    public DERInteger(
+        byte[]   bytes)
+    {
+        this.bytes = bytes;
+    }
+
+    public BigInteger getValue()
+    {
+        return new BigInteger(bytes);
+    }
+
+    /**
+     * in some cases positive values get crammed into a space,
+     * that's not quite big enough...
+     */
+    public BigInteger getPositiveValue()
+    {
+        return new BigInteger(1, bytes);
+    }
+
+    void encode(
+        DEROutputStream out)
+        throws IOException
+    {
+        out.writeEncoded(INTEGER, bytes);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERObject.java b/src/org/bouncycastle/asn1/DERObject.java
new file mode 100644 (file)
index 0000000..fb75442
--- /dev/null
@@ -0,0 +1,15 @@
+package org.bouncycastle.asn1;
+
+import java.io.IOException;
+
+public abstract class DERObject
+    implements DERTags, DEREncodable
+{
+    abstract void encode(DEROutputStream out)
+        throws IOException;
+
+    public DERObject getDERObject()
+    {
+        return this;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERObjectIdentifier.java b/src/org/bouncycastle/asn1/DERObjectIdentifier.java
new file mode 100644 (file)
index 0000000..b22c7ae
--- /dev/null
@@ -0,0 +1,77 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+
+public class DERObjectIdentifier
+    extends DERObject
+{
+    String      identifier;
+
+    public DERObjectIdentifier(
+        String  identifier)
+    {
+        this.identifier = identifier;
+    }
+
+    public String getId()
+    {
+        return identifier;
+    }
+
+    void encode(
+        DEROutputStream out)
+        throws IOException
+    {
+        OIDTokenizer            tok = new OIDTokenizer(identifier);
+        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
+        DEROutputStream         dOut = new DEROutputStream(bOut);
+
+                                    // space for 5 7 bit numbers in an int
+        byte[]                  iBuf = new byte[5];    
+        
+        dOut.write(
+                    Integer.parseInt(tok.nextToken()) * 40
+                    + Integer.parseInt(tok.nextToken()));
+
+        while (tok.hasMoreTokens())
+        {
+            //
+            // translate into base 128
+            //
+            int value = Integer.parseInt(tok.nextToken());
+            int count = iBuf.length - 1;
+            
+            iBuf[count--] = (byte)(value % 128);
+            value /= 128;
+
+            while (value != 0)
+            {
+                iBuf[count--] = (byte)((value % 128) | 0x80);
+                value /= 128;
+            }
+            dOut.write(iBuf, count + 1, iBuf.length - (count + 1));
+        }
+
+        dOut.close();
+
+        byte[]  bytes = bOut.toByteArray();
+
+        out.writeEncoded(OBJECT_IDENTIFIER, bytes);
+    }
+
+    public int hashCode()
+    {
+        return identifier.hashCode();
+    }
+
+    public boolean equals(
+        Object  o)
+    {
+        if ((o == null) || !(o instanceof DERObjectIdentifier))
+        {
+            return false;
+        }
+
+        return identifier.equals(((DERObjectIdentifier)o).identifier);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DEROctetString.java b/src/org/bouncycastle/asn1/DEROctetString.java
new file mode 100644 (file)
index 0000000..fd9f516
--- /dev/null
@@ -0,0 +1,97 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+
+public class DEROctetString
+    extends DERObject
+{
+    byte[]  string;
+
+    /**
+     * @param string the octets making up the octet string.
+     */
+    public DEROctetString(
+        byte[]  string)
+    {
+        this.string = string;
+    }
+
+    public DEROctetString(
+        DERObject  obj)
+    {
+        try
+        {
+            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
+            DEROutputStream         dOut = new DEROutputStream(bOut);
+
+            dOut.writeObject(obj);
+            dOut.close();
+
+            this.string = bOut.toByteArray();
+        }
+        catch (IOException e)
+        {
+            throw new IllegalArgumentException("Error processing object : " + e.toString());
+        }
+    }
+
+    public DEROctetString(
+        DEREncodable  obj)
+    {
+        this(obj.getDERObject());
+    }
+
+    public byte[] getOctets()
+    {
+        return string;
+    }
+
+    void encode(
+        DEROutputStream out)
+        throws IOException
+    {
+        out.writeEncoded(OCTET_STRING, string);
+    }
+
+    public int hashCode()
+    {
+        byte[]  b = this.getOctets();
+        int     value = 0;
+
+        for (int i = 0; i != b.length; i++)
+        {
+            value ^= (b[i] & 0xff) << (i % 4);
+        }
+
+        return value;
+    }
+
+    public boolean equals(
+        Object  o)
+    {
+        if (o == null || !(o instanceof DEROctetString))
+        {
+            return false;
+        }
+
+        DEROctetString  other = (DEROctetString)o;
+
+        if (other.getOctets().length != this.getOctets().length)
+        {
+            return false;
+        }
+
+        byte[]  b1 = other.getOctets();
+        byte[]  b2 = this.getOctets();
+
+        for (int i = 0; i != b1.length; i++)
+        {
+            if (b1[i] != b2[i])
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DEROutputStream.java b/src/org/bouncycastle/asn1/DEROutputStream.java
new file mode 100644 (file)
index 0000000..03e77a2
--- /dev/null
@@ -0,0 +1,79 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+
+public class DEROutputStream
+    extends FilterOutputStream implements DERTags
+{
+    public DEROutputStream(
+        OutputStream    os)
+    {
+        super(os);
+    }
+
+    private void writeLength(
+        int length)
+        throws IOException
+    {
+        if (length > 127)
+        {
+            int size = 1;
+            int val = length;
+
+            while ((val >>>= 8) != 0)
+            {
+                size++;
+            }
+
+            write((byte)(size | 0x80));
+
+            for (int i = (size - 1) * 8; i >= 0; i -= 8)
+            {
+                write((byte)(length >> i));
+            }
+        }
+        else
+        {
+            write((byte)length);
+        }
+    }
+
+    void writeEncoded(
+        int     tag,
+        byte[]  bytes)
+        throws IOException
+    {
+        write(tag);
+        writeLength(bytes.length);
+        write(bytes);
+    }
+
+    protected void writeNull()
+        throws IOException
+    {
+        write(NULL);
+        write(0x00);
+    }
+
+    public void writeObject(
+        Object    obj)
+        throws IOException
+    {
+        if (obj == null)
+        {
+            writeNull();
+        }
+        else if (obj instanceof DERObject)
+        {
+            ((DERObject)obj).encode(this);
+        }
+        else if (obj instanceof DEREncodable)
+        {
+            ((DEREncodable)obj).getDERObject().encode(this);
+        }
+        else 
+        {
+            throw new IOException("object not DEREncodable");
+        }
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERPrintableString.java b/src/org/bouncycastle/asn1/DERPrintableString.java
new file mode 100644 (file)
index 0000000..7834edf
--- /dev/null
@@ -0,0 +1,62 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+
+/**
+ * DER PrintableString object.
+ */
+public class DERPrintableString
+    extends DERObject
+    implements DERString
+{
+    String  string;
+
+    /**
+     * basic constructor - byte encoded string.
+     */
+    public DERPrintableString(
+        byte[]   string)
+    {
+        try
+        {
+            this.string = new String(string, "US-ASCII");
+        }
+        catch(UnsupportedEncodingException e)
+        {
+            throw new RuntimeException("PANIC: " + e);
+        }
+    }
+
+    /**
+     * basic constructor
+     */
+    public DERPrintableString(
+        String   string)
+    {
+        this.string = string;
+    }
+
+    public String getString()
+    {
+        return string;
+    }
+
+    public byte[] getOctets()
+    {
+        try
+        {
+            return string.getBytes("US-ASCII");
+        }
+        catch(UnsupportedEncodingException e)
+        {
+            throw new RuntimeException("PANIC: " + e);
+        }
+    }
+
+    void encode(
+        DEROutputStream  out)
+        throws IOException
+    {
+        out.writeEncoded(PRINTABLE_STRING, this.getOctets());
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERSet.java b/src/org/bouncycastle/asn1/DERSet.java
new file mode 100644 (file)
index 0000000..2d2caf4
--- /dev/null
@@ -0,0 +1,24 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+
+/**
+ * DER Set with a single object.
+ */
+public class DERSet
+    extends DERConstructedSet
+{
+    /**
+     * @param sequence the sequence making up the set
+     */
+    public DERSet(
+        DEREncodable   sequence)
+    {
+        this.addObject(sequence);
+    }
+
+    public DERObject getSequence()
+    {
+        return (DERObject)this.getObjectAt(0);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERString.java b/src/org/bouncycastle/asn1/DERString.java
new file mode 100644 (file)
index 0000000..3143be9
--- /dev/null
@@ -0,0 +1,9 @@
+package org.bouncycastle.asn1;
+
+/**
+ * basic interface for DER string objects.
+ */
+public interface DERString
+{
+    public String getString();
+}
diff --git a/src/org/bouncycastle/asn1/DERT61String.java b/src/org/bouncycastle/asn1/DERT61String.java
new file mode 100644 (file)
index 0000000..4412efc
--- /dev/null
@@ -0,0 +1,43 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+
+/**
+ * DER T61String (also the teletex string)
+ */
+public class DERT61String
+    extends DERObject
+    implements DERString
+{
+    String  string;
+
+    /**
+     * basic constructor - with bytes.
+     */
+    public DERT61String(
+        byte[]   string)
+    {
+        this.string = new String(string);
+    }
+
+    /**
+     * basic constructor - with string.
+     */
+    public DERT61String(
+        String   string)
+    {
+        this.string = string;
+    }
+
+    public String getString()
+    {
+        return string;
+    }
+
+    void encode(
+        DEROutputStream  out)
+        throws IOException
+    {
+        out.writeEncoded(T61_STRING, string.getBytes());
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERTaggedObject.java b/src/org/bouncycastle/asn1/DERTaggedObject.java
new file mode 100644 (file)
index 0000000..4ec0310
--- /dev/null
@@ -0,0 +1,119 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+
+/**
+ * DER TaggedObject - in ASN.1 nottation this is any object proceeded by
+ * a [n] where n is some number - these are assume to follow the construction
+ * rules (as with sequences).
+ */
+public class DERTaggedObject
+    extends DERObject
+{
+    int             tagNo;
+    boolean         empty = false;
+    boolean         explicit = true;
+    DEREncodable    obj = null;
+
+    /**
+     * This creates an empty tagged object of tagNo (ie. zero length).
+     *
+     * @param tagNo the tag number for this object.
+     */
+    public DERTaggedObject(
+        int     tagNo)
+    {
+        this.explicit = true;
+        this.tagNo = tagNo;
+        this.empty = true; 
+    }
+
+    /**
+     * @param tagNo the tag number for this object.
+     * @param obj the tagged object.
+     */
+    public DERTaggedObject(
+        int             tagNo,
+        DEREncodable    obj)
+    {
+        this.explicit = true;
+        this.tagNo = tagNo;
+        this.obj = obj;
+    }
+
+    /**
+     * @param explicit true if the object is explicitly tagged.
+     * @param tagNo the tag number for this object.
+     * @param obj the tagged object.
+     */
+    public DERTaggedObject(
+        boolean         explicit,
+        int             tagNo,
+        DEREncodable    obj)
+    {
+        this.explicit = explicit;
+        this.tagNo = tagNo;
+        this.obj = obj;
+    }
+
+    public int getTagNo()
+    {
+        return tagNo;
+    }
+
+    public boolean isExplicit()
+    {
+        return explicit;
+    }
+
+    public boolean isEmpty()
+    {
+        return empty;
+    }
+
+    public DERObject getObject()
+    {
+        return obj.getDERObject();
+    }
+
+    void encode(
+        DEROutputStream  out)
+        throws IOException
+    {
+        if (!empty)
+        {
+            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
+            DEROutputStream         dOut = new DEROutputStream(bOut);
+
+            dOut.writeObject(obj);
+            dOut.close();
+
+            byte[]  bytes = bOut.toByteArray();
+
+            if (explicit)
+            {
+                out.writeEncoded(CONSTRUCTED | TAGGED | tagNo, bOut.toByteArray());
+            }
+            else
+            {
+                //
+                // need to mark constructed types...
+                //
+                if ((bytes[0] & CONSTRUCTED) != 0)
+                {
+                    bytes[0] = (byte)(CONSTRUCTED | TAGGED | tagNo);
+                }
+                else
+                {
+                    bytes[0] = (byte)(TAGGED | tagNo);
+                }
+
+                out.write(bytes);
+            }
+        }
+        else
+        {
+            out.writeEncoded(CONSTRUCTED | TAGGED | tagNo, new byte[0]);
+        }
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERTags.java b/src/org/bouncycastle/asn1/DERTags.java
new file mode 100644 (file)
index 0000000..085b787
--- /dev/null
@@ -0,0 +1,30 @@
+package org.bouncycastle.asn1;
+
+public interface DERTags
+{
+    public static final int BOOLEAN             = 0x01;
+    public static final int INTEGER             = 0x02;
+    public static final int BIT_STRING          = 0x03;
+    public static final int OCTET_STRING        = 0x04;
+    public static final int NULL                = 0x05;
+    public static final int OBJECT_IDENTIFIER   = 0x06;
+    public static final int EXTERNAL            = 0x08;
+    public static final int SEQUENCE            = 0x10;
+    public static final int SEQUENCE_OF         = 0x10; // for completeness
+    public static final int SET                 = 0x11;
+    public static final int SET_OF              = 0x11; // for completeness
+    public static final int CONSTRUCTED         = 0x20;
+    public static final int TAGGED              = 0x80;
+
+    public static final int NUMERIC_STRING      = 0x12;
+    public static final int PRINTABLE_STRING    = 0x13;
+    public static final int T61_STRING          = 0x14;
+    public static final int VIDEOTEX_STRING     = 0x15;
+    public static final int IA5_STRING          = 0x16;
+    public static final int UTC_TIME            = 0x17;
+    public static final int GENERALIZED_TIME    = 0x18;
+    public static final int GRAPHIC_STRING      = 0x19;
+    public static final int VISIBLE_STRING      = 0x1a;
+    public static final int GENERAL_STRING      = 0x1b;
+    public static final int BMP_STRING          = 0x1e;
+}
diff --git a/src/org/bouncycastle/asn1/DERUTCTime.java b/src/org/bouncycastle/asn1/DERUTCTime.java
new file mode 100644 (file)
index 0000000..061bf54
--- /dev/null
@@ -0,0 +1,79 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+import java.util.*;
+import java.io.*;
+
+/**
+ * UTC time object.
+ */
+public class DERUTCTime
+    extends DERObject
+{
+    String      time;
+
+    /**
+     * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were
+     * never encoded. When you're creating one of these objects from scratch, that's
+     * what you want to use, otherwise we'll try to deal with whatever gets read from
+     * the input stream... (this is why the input format is different from the getTime()
+     * method output).
+     * <p>
+     * You can generate a Java date string in the right format by using:
+     * <pre>
+     *      dateF = new SimpleDateFormat("yyMMddHHmmss");
+     *      tz = new SimpleTimeZone(0, "Z");
+     *     
+     *      dateF.setTimeZone(tz);
+     *
+     *      utcTime = new DERUTCTime(dateF.format(new Date()) + "Z");
+     * </pre>
+     *
+     * @param time the time string.
+     */
+    public DERUTCTime(
+        String  time)
+    {
+        this.time = time;
+    }
+
+    /**
+     * return the time - always in the form of 
+     *  YYMMDDhhmmssGMT(+hh:mm|-hh:mm).
+     * <p>
+     * Normally in a certificate we would expect "Z" rather than "GMT",
+     * however adding the "GMT" means we can just use:
+     * <pre>
+     *     dateF = new SimpleDateFormat("yyMMddHHmmssz");
+     * </pre>
+     * To read in the time and get a date which is compatible with our local
+     * time zone.
+     */
+    public String getTime()
+    {
+        //
+        // standardise the format.
+        //
+        if (time.length() == 11)
+        {
+            return time.substring(0, 10) + "00GMT+00:00";
+        }
+        else if (time.length() == 13)
+        {
+            return time.substring(0, 12) + "GMT+00:00";
+        }
+        else if (time.length() == 17)
+        {
+            return time.substring(0, 12) + "GMT" + time.substring(12, 15) + ":" + time.substring(15, 17);
+        }
+
+        return time;
+    }
+
+    void encode(
+        DEROutputStream  out)
+        throws IOException
+    {
+        out.writeEncoded(UTC_TIME, time.getBytes());
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERUnknownTag.java b/src/org/bouncycastle/asn1/DERUnknownTag.java
new file mode 100644 (file)
index 0000000..c5f46f9
--- /dev/null
@@ -0,0 +1,42 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+
+/**
+ * We insert one of these when we find a tag we don't recognise.
+ */
+public class DERUnknownTag
+    extends DERObject
+{
+    int         tag;
+    byte[]      data;
+
+    /**
+     * @param tag the tag value.
+     * @param data the octets making up the time.
+     */
+    public DERUnknownTag(
+        int     tag,
+        byte[]  data)
+    {
+        this.tag = tag;
+        this.data = data;
+    }
+
+    public int getTag()
+    {
+        return tag;
+    }
+
+    public byte[] getData()
+    {
+        return data;
+    }
+
+    void encode(
+        DEROutputStream  out)
+        throws IOException
+    {
+        out.writeEncoded(tag, data);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/DERVisibleString.java b/src/org/bouncycastle/asn1/DERVisibleString.java
new file mode 100644 (file)
index 0000000..f2071d3
--- /dev/null
@@ -0,0 +1,62 @@
+package org.bouncycastle.asn1;
+
+import java.io.*;
+
+/**
+ * DER VisibleString object.
+ */
+public class DERVisibleString
+    extends DERObject
+    implements DERString
+{
+    String  string;
+
+    /**
+     * basic constructor - byte encoded string.
+     */
+    public DERVisibleString(
+        byte[]   string)
+    {
+        try
+        {
+            this.string = new String(string, "US-ASCII");
+        }
+        catch(UnsupportedEncodingException e)
+        {
+            throw new RuntimeException("PANIC: " + e);
+        }
+    }
+
+    /**
+     * basic constructor
+     */
+    public DERVisibleString(
+        String   string)
+    {
+        this.string = string;
+    }
+
+    public String getString()
+    {
+        return string;
+    }
+
+    public byte[] getOctets()
+    {
+        try
+        {
+            return string.getBytes("US-ASCII");
+        }
+        catch(UnsupportedEncodingException e)
+        {
+            throw new RuntimeException("PANIC: " + e);
+        }
+    }
+
+    void encode(
+        DEROutputStream  out)
+        throws IOException
+    {
+        out.writeEncoded(VISIBLE_STRING, this.getOctets());
+    }
+}
diff --git a/src/org/bouncycastle/asn1/OIDTokenizer.java b/src/org/bouncycastle/asn1/OIDTokenizer.java
new file mode 100644 (file)
index 0000000..5467944
--- /dev/null
@@ -0,0 +1,48 @@
+package org.bouncycastle.asn1;
+
+/**
+ * class for breaking up an OID into it's component tokens, ala
+ * java.util.StringTokenizer. We need this class as some of the
+ * lightweight Java environment don't support classes like
+ * StringTokenizer.
+ */
+public class OIDTokenizer
+{
+    private String  oid;
+    private int     index;
+
+    public OIDTokenizer(
+        String oid)
+    {
+        this.oid = oid;
+        this.index = 0;
+    }
+
+    public boolean hasMoreTokens()
+    {
+        return (index != -1);
+    }
+
+    public String nextToken()
+    {
+        if (index == -1)
+        {
+            return null;
+        }
+
+        String  token;
+        int     end = oid.indexOf('.', index);
+
+        if (end == -1)
+        {
+            token = oid.substring(index);
+            index = -1;
+            return token;
+        }
+
+        token = oid.substring(index, end);
+
+        index = end + 1;
+        return token;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java b/src/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
new file mode 100644 (file)
index 0000000..f471332
--- /dev/null
@@ -0,0 +1,98 @@
+package org.bouncycastle.asn1.pkcs;
+
+import org.bouncycastle.asn1.DERObjectIdentifier;
+
+public interface PKCSObjectIdentifiers
+{
+    //
+    // pkcs-1 OBJECT IDENTIFIER ::= {
+    //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
+    //
+    static final String                 pkcs_1                  = "1.2.840.113549.1.1";
+    static final DERObjectIdentifier    rsaEncryption           = new DERObjectIdentifier(pkcs_1 + ".1");
+    static final DERObjectIdentifier    md2WithRSAEncryption    = new DERObjectIdentifier(pkcs_1 + ".2");
+    static final DERObjectIdentifier    md4WithRSAEncryption    = new DERObjectIdentifier(pkcs_1 + ".3");
+    static final DERObjectIdentifier    md5WithRSAEncryption    = new DERObjectIdentifier(pkcs_1 + ".4");
+    static final DERObjectIdentifier    sha1WithRSAEncryption   = new DERObjectIdentifier(pkcs_1 + ".5");
+    static final DERObjectIdentifier    srsaOAEPEncryptionSET   = new DERObjectIdentifier(pkcs_1 + ".6");
+
+    //
+    // pkcs-3 OBJECT IDENTIFIER ::= {
+    //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 3 }
+    //
+    static final String                 pkcs_3                  = "1.2.840.113549.1.3";
+    static final DERObjectIdentifier    dhKeyAgreement          = new DERObjectIdentifier(pkcs_3 + ".1");
+
+    //
+    // pkcs-5 OBJECT IDENTIFIER ::= {
+    //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 5 }
+    //
+    static final String                 pkcs_5                  = "1.2.840.113549.1.5";
+
+    static final DERObjectIdentifier    id_PBES2                = new DERObjectIdentifier(pkcs_5 + ".13");
+
+    static final DERObjectIdentifier    id_PBKDF2               = new DERObjectIdentifier(pkcs_5 + ".12");
+
+    //
+    // encryptionAlgorithm OBJECT IDENTIFIER ::= {
+    //       iso(1) member-body(2) us(840) rsadsi(113549) 3 }
+    //
+    static final String                 encryptionAlgorithm     = "1.2.840.113549.3";
+
+    static final DERObjectIdentifier    des_EDE3_CBC            = new DERObjectIdentifier(encryptionAlgorithm + ".7");
+    static final DERObjectIdentifier    RC2_CBC                 = new DERObjectIdentifier(encryptionAlgorithm + ".2");
+
+    //
+    // object identifiers for digests
+    //
+
+    //
+    // md2 OBJECT IDENTIFIER ::=
+    //      {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 2}
+    //
+    static final DERObjectIdentifier    md2                     = new DERObjectIdentifier("1.2.840.113549.2.2");
+
+    //
+    // md5 OBJECT IDENTIFIER ::=
+    //      {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 5}
+    //
+    static final DERObjectIdentifier    md5                     = new DERObjectIdentifier("1.2.840.113549.2.5");
+
+    //
+    // pkcs-7 OBJECT IDENTIFIER ::= {
+    //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 7 }
+    //
+    static final String                 pkcs_7                  = "1.2.840.113549.1.7";
+    static final DERObjectIdentifier    data                    = new DERObjectIdentifier(pkcs_7 + ".1");
+    static final DERObjectIdentifier    signedData              = new DERObjectIdentifier(pkcs_7 + ".2");
+    static final DERObjectIdentifier    envelopedData           = new DERObjectIdentifier(pkcs_7 + ".3");
+    static final DERObjectIdentifier    signedAndEnvelopedData  = new DERObjectIdentifier(pkcs_7 + ".4");
+    static final DERObjectIdentifier    digestedData            = new DERObjectIdentifier(pkcs_7 + ".5");
+    static final DERObjectIdentifier    encryptedData           = new DERObjectIdentifier(pkcs_7 + ".6");
+
+    //
+    // pkcs-9 OBJECT IDENTIFIER ::= {
+    //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 }
+    //
+    static final String                 pkcs_9                  = "1.2.840.113549.1.9";
+
+    static final DERObjectIdentifier    pkcs_9_at_emailAddress  = new DERObjectIdentifier(pkcs_9 + ".1");
+    static final DERObjectIdentifier    pkcs_9_at_friendlyName  = new DERObjectIdentifier(pkcs_9 + ".20");
+    static final DERObjectIdentifier    pkcs_9_at_localKeyId    = new DERObjectIdentifier(pkcs_9 + ".21");
+    static final DERObjectIdentifier    x509certType            = new DERObjectIdentifier(pkcs_9 + ".22.1");
+
+    //
+    // pkcs-12 OBJECT IDENTIFIER ::= {
+    //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 12 }
+    //
+    static final String                 pkcs_12                  = "1.2.840.113549.1.12";
+    static final String                 bagtypes                 = pkcs_12 + ".10.1";
+
+    static final DERObjectIdentifier    keyBag                  = new DERObjectIdentifier(bagtypes + ".1");
+    static final DERObjectIdentifier    pkcs8ShroudedKeyBag     = new DERObjectIdentifier(bagtypes + ".2");
+    static final DERObjectIdentifier    certBag                 = new DERObjectIdentifier(bagtypes + ".3");
+    static final DERObjectIdentifier    crlBag                  = new DERObjectIdentifier(bagtypes + ".4");
+    static final DERObjectIdentifier    secretBag               = new DERObjectIdentifier(bagtypes + ".5");
+    static final DERObjectIdentifier    safeContentsBag         = new DERObjectIdentifier(bagtypes + ".6");
+}
+
diff --git a/src/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java b/src/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java
new file mode 100644 (file)
index 0000000..cad4c82
--- /dev/null
@@ -0,0 +1,136 @@
+package org.bouncycastle.asn1.x509;
+
+import java.io.*;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.*;
+
+public class AlgorithmIdentifier
+    implements DEREncodable
+{
+    private DERObjectIdentifier objectId;
+    private DERObject           parameters;
+    private boolean             parametersDefined = false;
+
+    public AlgorithmIdentifier(
+        DERObjectIdentifier     objectId)
+    {
+        this.objectId = objectId;
+    }
+
+    public AlgorithmIdentifier(
+        DERObjectIdentifier     objectId,
+        DERObject               parameters)
+    {
+        parametersDefined = true;
+
+        this.objectId = objectId;
+        this.parameters = parameters;
+    }
+
+    public AlgorithmIdentifier(
+        DERConstructedSequence  obj)
+    {
+        objectId = (DERObjectIdentifier)obj.getObjectAt(0);
+
+        if (obj.getSize() == 2)
+        {
+            parametersDefined = true;
+            parameters = (DERObject)obj.getObjectAt(1);
+        }
+        else
+        {
+            parameters = null;
+        }
+    }
+
+    public DERObjectIdentifier getObjectId()
+    {
+        return objectId;
+    }
+
+    public DERObject getParameters()
+    {
+        return parameters;
+    }
+
+    /**
+     * <pre>
+     *      AlgorithmIdentifier ::= SEQUENCE {
+     *                            algorithm OBJECT IDENTIFIER,
+     *                            parameters ANY DEFINED BY algorithm OPTIONAL }
+     * </pre>
+     */
+    public DERObject getDERObject()
+    {
+        DERConstructedSequence  seq = new DERConstructedSequence();
+
+        seq.addObject(objectId);
+
+        if (parametersDefined)
+        {
+            seq.addObject(parameters);
+        }
+
+        return seq;
+    }
+
+    public boolean equals(
+        Object  o)
+    {
+        if ((o == null) || !(o instanceof AlgorithmIdentifier))
+        {
+            return false;
+        }
+
+        AlgorithmIdentifier other = (AlgorithmIdentifier)o;
+
+        if (!this.getObjectId().equals(other.getObjectId()))
+        {
+            return false;
+        }
+
+        if (this.getParameters() == null && other.getParameters() == null)
+        {
+            return true;
+        }
+
+        if (this.getParameters() == null || other.getParameters() == null)
+        {
+            return false;
+        }
+
+        ByteArrayOutputStream   b1Out = new ByteArrayOutputStream();
+        ByteArrayOutputStream   b2Out = new ByteArrayOutputStream();
+        DEROutputStream         d1Out = new DEROutputStream(b1Out);
+        DEROutputStream         d2Out = new DEROutputStream(b2Out);
+
+        try
+        {
+            d1Out.writeObject(this.getParameters());
+            d2Out.writeObject(other.getParameters());
+
+            byte[]  b1 = b1Out.toByteArray();
+            byte[]  b2 = b2Out.toByteArray();
+
+            if (b1.length != b2.length)
+            {
+                return false;
+            }
+
+            for (int i = 0; i != b1.length; i++)
+            {
+                if (b1[i] != b2[i])
+                {
+                    return false;
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java b/src/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java
new file mode 100644 (file)
index 0000000..9285c96
--- /dev/null
@@ -0,0 +1,172 @@
+package org.bouncycastle.asn1.x509;
+
+import java.math.BigInteger;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.asn1.*;
+
+/**
+ * <pre>
+ * id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::=  { id-ce 35 }
+ *
+ *   AuthorityKeyIdentifier ::= SEQUENCE {
+ *      keyIdentifier             [0] IMPLICIT KeyIdentifier           OPTIONAL,
+ *      authorityCertIssuer       [1] IMPLICIT GeneralNames            OPTIONAL,
+ *      authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL  }
+ *
+ *   KeyIdentifier ::= OCTET STRING
+ * </pre>
+ *
+ */
+public class AuthorityKeyIdentifier
+    implements DEREncodable
+{
+    DEROctetString keyidentifier=null;
+    GeneralNames certissuer=null;
+    DERInteger certserno=null;
+
+    public AuthorityKeyIdentifier(
+        DERConstructedSequence   seq)
+    {
+        Enumeration e = seq.getObjects();
+
+        while (e.hasMoreElements())
+        {
+            DERTaggedObject o = (DERTaggedObject)e.nextElement();
+
+            switch (o.getTagNo())
+            {
+            case 0:
+                this.keyidentifier= (DEROctetString)o.getObject();
+                break;
+
+            case 1:
+                if (o.getObject() instanceof DERConstructedSequence)
+                {
+                    this.certissuer = new GeneralNames((DERConstructedSequence)o.getObject());
+                }
+                else   
+                {
+                    // as it's implicitly tagged we can loose the"sequence"
+                    // if there is only one object.
+                    //
+                    DERConstructedSequence s = new DERConstructedSequence();
+
+                    s.addObject(o.getObject());
+
+                    this.certissuer = new GeneralNames(s);
+                }
+                break;
+            case 2:
+                //
+                // implicit tagging again...
+                //
+                DEROctetString          oct = (DEROctetString)o.getObject();
+
+                this.certserno = new DERInteger(new BigInteger(oct.getOctets()));
+                break;
+            default:
+                throw new IllegalArgumentException("illegal tag");
+            }
+        }
+    }
+
+    /**
+     *
+     * Calulates the keyidentifier using a SHA1 hash over the BIT STRING
+     * from SubjectPublicKeyInfo as defined in RFC2459.
+     *
+     * Example of making a AuthorityKeyIdentifier:
+     * <pre>
+     *   SubjectPublicKeyInfo apki = new SubjectPublicKeyInfo((DERConstructedSequence)new DERInputStream(
+     *       new ByteArrayInputStream(publicKey.getEncoded())).readObject());
+     *   AuthorityKeyIdentifier aki = new AuthorityKeyIdentifier(apki);
+     * </pre>
+     *
+     **/
+    public AuthorityKeyIdentifier(
+        SubjectPublicKeyInfo    spki)
+    {
+        Digest  digest = new SHA1Digest();
+        byte[]  resBuf = new byte[digest.getDigestSize()];
+
+        DERBitString derpk = new DERBitString(spki.getPublicKey());
+        byte[] bytes = derpk.getBytes();
+        digest.update(bytes, 0, bytes.length);
+        digest.doFinal(resBuf, 0);
+        this.keyidentifier=new DEROctetString(resBuf);
+    }
+
+    /**
+     * create an AuthorityKeyIdentifier with the GeneralNames tag and
+     * the serial number provided as well.
+     */
+    public AuthorityKeyIdentifier(
+        SubjectPublicKeyInfo    spki,
+        GeneralNames            name,
+        BigInteger              serialNumber)
+    {
+        Digest  digest = new SHA1Digest();
+        byte[]  resBuf = new byte[digest.getDigestSize()];
+
+        DERBitString derpk = new DERBitString(spki.getPublicKey());
+        byte[] bytes = derpk.getBytes();
+        digest.update(bytes, 0, bytes.length);
+        digest.doFinal(resBuf, 0);
+
+        this.keyidentifier = new DEROctetString(resBuf);
+        this.certissuer = name;
+        this.certserno = new DERInteger(serialNumber);
+    }
+
+    public byte[] getKeyIdentifier()
+    {
+        if (keyidentifier != null)
+        {
+            return keyidentifier.getOctets();
+        }
+
+        return null;
+    }
+
+     /**
+     * <pre>
+     *   AuthorityKeyIdentifier ::= SEQUENCE {
+     *      keyIdentifier             [0] IMPLICIT KeyIdentifier           OPTIONAL,
+     *      authorityCertIssuer       [1] IMPLICIT GeneralNames            OPTIONAL,
+     *      authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL  }
+     *
+     *   KeyIdentifier ::= OCTET STRING
+     * </pre>
+     */
+    public DERObject getDERObject()
+    {
+        DERConstructedSequence  seq = new DERConstructedSequence();
+
+        if (keyidentifier != null)
+        {
+            seq.addObject(new DERTaggedObject(false, 0, keyidentifier));
+        }
+
+        if (certissuer != null)
+        {
+            seq.addObject(new DERTaggedObject(false, 1, certissuer));
+        }
+
+        if (certserno != null)
+        {
+            seq.addObject(new DERTaggedObject(false, 2, certserno));
+        }
+
+
+        return seq;
+    }
+
+    public String toString()
+    {
+        return ("AuthorityKeyIdentifier: KeyID(" + this.keyidentifier.getOctets() + ")");
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/BasicConstraints.java b/src/org/bouncycastle/asn1/x509/BasicConstraints.java
new file mode 100644 (file)
index 0000000..ec84915
--- /dev/null
@@ -0,0 +1,79 @@
+package org.bouncycastle.asn1.x509;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.*;
+
+public class BasicConstraints
+    implements DEREncodable
+{
+    DERBoolean  cA = new DERBoolean(false);
+    DERInteger  pathLenConstraint = null;
+
+    public BasicConstraints(
+        DERConstructedSequence   seq)
+    {
+        if (seq.getSize() != 0)
+        {
+            this.cA = (DERBoolean)seq.getObjectAt(0);
+            this.pathLenConstraint = (DERInteger)seq.getObjectAt(1);
+        }
+    }
+
+    public BasicConstraints(
+        boolean cA,
+        int     pathLenConstraint)
+    {
+        this.cA = new DERBoolean(cA);
+        this.pathLenConstraint = new DERInteger(pathLenConstraint);
+    }
+
+    public BasicConstraints(
+        boolean cA)
+    {
+        this.cA = new DERBoolean(cA);
+        this.pathLenConstraint = null;
+    }
+
+    public boolean isCA()
+    {
+        return cA.isTrue();
+    }
+
+    public BigInteger getPathLenConstraint()
+    {
+        if (pathLenConstraint != null)
+        {
+            return pathLenConstraint.getValue();
+        }
+
+        return null;
+    }
+
+    /**
+     * <pre>
+     * BasicConstraints := SEQUENCE {
+     *    cA                  BOOLEAN DEFAULT FALSE,
+     *    pathLenConstraint   INTEGER (0..MAX) OPTIONAL
+     * }
+     * </pre>
+     */
+    public DERObject getDERObject()
+    {
+        DERConstructedSequence  seq = new DERConstructedSequence();
+
+        seq.addObject(cA);
+
+        if (pathLenConstraint != null)
+        {
+            seq.addObject(pathLenConstraint);
+        }
+
+        return seq;
+    }
+
+    public String toString()
+    {
+        return "BasicConstraints: isCa(" + this.isCA() + "), pathLenConstraint = " + pathLenConstraint.getValue();
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/CRLDistPoint.java b/src/org/bouncycastle/asn1/x509/CRLDistPoint.java
new file mode 100644 (file)
index 0000000..780ab16
--- /dev/null
@@ -0,0 +1,30 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.*;
+
+public class CRLDistPoint
+    implements DEREncodable
+{
+    DERConstructedSequence  seq = null;
+
+    public CRLDistPoint(
+        DistributionPoint[] points)
+    {
+        seq = new DERConstructedSequence();
+
+        for (int i = 0; i != points.length; i++)
+        {
+            seq.addObject(points[i]);
+        }
+    }
+
+    /**
+     * <pre>
+     * CRLDistPoint ::= SEQUENCE SIZE {1..MAX} OF DistributionPoint
+     * </pre>
+     */
+    public DERObject getDERObject()
+    {
+        return seq;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/CRLNumber.java b/src/org/bouncycastle/asn1/x509/CRLNumber.java
new file mode 100644 (file)
index 0000000..bf9b3c9
--- /dev/null
@@ -0,0 +1,26 @@
+package org.bouncycastle.asn1.x509;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.*;
+
+/**
+ * <pre>
+ * CRLNumber::= INTEGER(0..MAX)
+ * </pre>
+ */
+public class CRLNumber
+    extends DERInteger
+{
+
+    public CRLNumber(
+        BigInteger number)
+    {
+        super(number);
+    }
+
+    public BigInteger getCRLNumber()
+    {
+        return getPositiveValue();
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/CertificateList.java b/src/org/bouncycastle/asn1/x509/CertificateList.java
new file mode 100644 (file)
index 0000000..48703d1
--- /dev/null
@@ -0,0 +1,101 @@
+
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.*;
+import org.bouncycastle.asn1.pkcs.*;
+
+/**
+ * PKIX RFC-2459
+ *
+ * The X.509 v2 CRL syntax is as follows.  For signature calculation,
+ * the data that is to be signed is ASN.1 DER encoded.
+ *
+ * <pre>
+ * CertificateList  ::=  SEQUENCE  {
+ *      tbsCertList          TBSCertList,
+ *      signatureAlgorithm   AlgorithmIdentifier,
+ *      signatureValue       BIT STRING  }
+ * </pre>
+ */
+
+public class CertificateList
+       implements DEREncodable
+{
+       DERConstructedSequence seq;
+
+       TBSCertList                     tbsCertList;
+       AlgorithmIdentifier     sigAlgId;
+       DERBitString            sig;
+
+    public CertificateList(
+        DERConstructedSequence seq)
+    {
+               this.seq = seq;
+
+               if ( seq.getObjectAt(0) instanceof TBSCertList )
+               {
+                       tbsCertList = (TBSCertList)seq.getObjectAt(0);
+               }
+               else
+               {
+                       tbsCertList = new TBSCertList((DERConstructedSequence)seq.getObjectAt(0));
+               }
+
+               if ( seq.getObjectAt(1) instanceof AlgorithmIdentifier )
+               {
+                       sigAlgId = (AlgorithmIdentifier)seq.getObjectAt(1);
+               }
+               else
+               {
+                       sigAlgId = new AlgorithmIdentifier((DERConstructedSequence)seq.getObjectAt(1));
+               }
+
+               sig = (DERBitString)seq.getObjectAt(2);
+       }
+
+       public TBSCertList getTBSCertList()
+       {
+               return tbsCertList;
+       }
+
+       public TBSCertList.CRLEntry[] getRevokedCertificates()
+       {
+               return tbsCertList.getRevokedCertificates();
+       }
+
+       public AlgorithmIdentifier getSignatureAlgorithm()
+       {
+               return sigAlgId;
+       }
+
+       public DERBitString getSignature()
+       {
+               return sig;
+       }
+
+       public int getVersion()
+       {
+               return tbsCertList.getVersion();
+       }
+
+       public X509Name getIssuer()
+       {
+               return tbsCertList.getIssuer();
+       }
+
+       public DERUTCTime getThisUpdate()
+       {
+               return tbsCertList.getThisUpdate();
+       }
+
+       public DERUTCTime getNextUpdate()
+       {
+               return tbsCertList.getNextUpdate();
+       }
+
+       public DERObject getDERObject()
+       {
+               return seq;
+       }
+}
+
diff --git a/src/org/bouncycastle/asn1/x509/DSAParameter.java b/src/org/bouncycastle/asn1/x509/DSAParameter.java
new file mode 100644 (file)
index 0000000..b6234bb
--- /dev/null
@@ -0,0 +1,58 @@
+package org.bouncycastle.asn1.x509;
+
+import java.math.*;
+import java.util.*;
+
+import org.bouncycastle.asn1.*;
+
+public class DSAParameter
+    implements DEREncodable
+{
+    DERInteger      p, q, g;
+
+    public DSAParameter(
+        BigInteger  p,
+        BigInteger  q,
+        BigInteger  g)
+    {
+        this.p = new DERInteger(p);
+        this.q = new DERInteger(q);
+        this.g = new DERInteger(g);
+    }
+
+    public DSAParameter(
+        DERConstructedSequence  seq)
+    {
+        Enumeration     e = seq.getObjects();
+
+        p = (DERInteger)e.nextElement();
+        q = (DERInteger)e.nextElement();
+        g = (DERInteger)e.nextElement();
+    }
+
+    public BigInteger getP()
+    {
+        return p.getPositiveValue();
+    }
+
+    public BigInteger getQ()
+    {
+        return q.getPositiveValue();
+    }
+
+    public BigInteger getG()
+    {
+        return g.getPositiveValue();
+    }
+
+    public DERObject getDERObject()
+    {
+        DERConstructedSequence  seq = new DERConstructedSequence();
+
+        seq.addObject(p);
+        seq.addObject(q);
+        seq.addObject(g);
+
+        return seq;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/DigestInfo.java b/src/org/bouncycastle/asn1/x509/DigestInfo.java
new file mode 100644 (file)
index 0000000..b15d37e
--- /dev/null
@@ -0,0 +1,57 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.*;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+
+/**
+ * <pre>
+ * DigestInfo::=SEQUENCE{
+ *          digestAlgorithm  AlgorithmIdentifier,
+ *          digest OCTET STRING }
+ * </pre>
+ */
+public class DigestInfo
+    implements PKCSObjectIdentifiers, DEREncodable
+{
+    private byte[]                  digest;
+    private AlgorithmIdentifier     algId;
+
+    public DigestInfo(
+        AlgorithmIdentifier  algId,
+        byte[]               digest)
+    {
+        this.digest = digest;
+        this.algId = algId;
+    }
+
+    public DigestInfo(
+        DERConstructedSequence  seq)
+    {
+        Enumeration             e = seq.getObjects();
+
+        algId = new AlgorithmIdentifier((DERConstructedSequence)e.nextElement());
+        digest = ((DEROctetString)e.nextElement()).getOctets();
+    }
+
+    public AlgorithmIdentifier getAlgorithmId()
+    {
+        return algId;
+    }
+
+    public byte[] getDigest()
+    {
+        return digest;
+    }
+
+    public DERObject getDERObject()
+    {
+        DERConstructedSequence  seq = new DERConstructedSequence();
+
+        seq.addObject(algId);
+        seq.addObject(new DEROctetString(digest));
+
+        return seq;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/DistributionPoint.java b/src/org/bouncycastle/asn1/x509/DistributionPoint.java
new file mode 100644 (file)
index 0000000..cb64d68
--- /dev/null
@@ -0,0 +1,46 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.*;
+
+public class DistributionPoint
+    implements DEREncodable
+{
+    DERConstructedSequence  seq = null;
+
+    public DistributionPoint(
+        DistributionPointName   distributionPoint,
+        ReasonFlags             reasons,
+        GeneralNames            cRLIssuer)
+    {
+        seq = new DERConstructedSequence();
+
+        if (distributionPoint != null)
+        {
+            seq.addObject(new DERTaggedObject(0, distributionPoint));
+        }
+
+        if (reasons != null)
+        {
+            seq.addObject(new DERTaggedObject(1, reasons));
+        }
+
+        if (cRLIssuer != null)
+        {
+            seq.addObject(new DERTaggedObject(2, cRLIssuer));
+        }
+    }
+
+    /**
+     * <pre>
+     * DistributionPoint ::= SEQUENCE {
+     *      distributionPoint [0] DistributionPointName OPTIONAL,
+     *      reasons           [1] ReasonFlags OPTIONAL,
+     *      cRLIssuer         [2] GeneralNames OPTIONAL
+     * }
+     * </pre>
+     */
+    public DERObject getDERObject()
+    {
+        return seq;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/DistributionPointName.java b/src/org/bouncycastle/asn1/x509/DistributionPointName.java
new file mode 100644 (file)
index 0000000..bac341e
--- /dev/null
@@ -0,0 +1,34 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.*;
+
+public class DistributionPointName
+    implements DEREncodable
+{
+    DEREncodable        name;
+    int                 type;
+
+    public static final int FULL_NAME = 0;
+    public static final int NAME_RELATIVE_TO_CRL_ISSUER = 1;
+
+    public DistributionPointName(
+        int             type,
+        DEREncodable    name)
+    {
+        this.type = type;
+        this.name = name;
+    }
+
+    /**
+     * <pre>
+     * DistributionPointName ::= CHOICE {
+     *     fullName                 [0] GeneralNames,
+     *     nameRelativeToCRLIssuer  [1] RelativeDistinguishedName
+     * }
+     * </pre>
+     */
+    public DERObject getDERObject()
+    {
+        return new DERTaggedObject(false, type, name);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/GeneralName.java b/src/org/bouncycastle/asn1/x509/GeneralName.java
new file mode 100644 (file)
index 0000000..41c2578
--- /dev/null
@@ -0,0 +1,78 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.*;
+
+/**
+ * <pre>
+ * GeneralName ::= CHOICE {
+ *      otherName                       [0]     OtherName,
+ *      rfc822Name                      [1]     IA5String,
+ *      dNSName                         [2]     IA5String,
+ *      x400Address                     [3]     ORAddress,
+ *      directoryName                   [4]     Name,
+ *      ediPartyName                    [5]     EDIPartyName,
+ *      uniformResourceIdentifier       [6]     IA5String,
+ *      iPAddress                       [7]     OCTET STRING,
+ *      registeredID                    [8]     OBJECT IDENTIFIER}
+ *
+ * OtherName ::= SEQUENCE {
+ *      type-id    OBJECT IDENTIFIER,
+ *      value      [0] EXPLICIT ANY DEFINED BY type-id }
+ *
+ * EDIPartyName ::= SEQUENCE {
+ *      nameAssigner            [0]     DirectoryString OPTIONAL,
+ *      partyName               [1]     DirectoryString }
+ * </pre>
+ */
+public class GeneralName
+    implements DEREncodable
+{
+    DEREncodable  obj;
+    int           tag;
+
+    public GeneralName(
+        X509Name  directoryName)
+    {
+        this.obj = directoryName;
+        this.tag = 4;
+    }
+
+    /**
+     * When the subjectAltName extension contains an Internet mail address,
+     * the address MUST be included as an rfc822Name. The format of an
+     * rfc822Name is an "addr-spec" as defined in RFC 822 [RFC 822].
+     *
+     * When the subjectAltName extension contains a domain name service
+     * label, the domain name MUST be stored in the dNSName (an IA5String).
+     * The name MUST be in the "preferred name syntax," as specified by RFC
+     * 1034 [RFC 1034].
+     *
+     * When the subjectAltName extension contains a URI, the name MUST be
+     * stored in the uniformResourceIdentifier (an IA5String). The name MUST
+     * be a non-relative URL, and MUST follow the URL syntax and encoding
+     * rules specified in [RFC 1738].  The name must include both a scheme
+     * (e.g., "http" or "ftp") and a scheme-specific-part.  The scheme-
+     * specific-part must include a fully qualified domain name or IP
+     * address as the host.
+     *
+     * When the subjectAltName extension contains a iPAddress, the address
+     * MUST be stored in the octet string in "network byte order," as
+     * specified in RFC 791 [RFC 791]. The least significant bit (LSB) of
+     * each octet is the LSB of the corresponding byte in the network
+     * address. For IP Version 4, as specified in RFC 791, the octet string
+     * MUST contain exactly four octets.  For IP Version 6, as specified in
+     * RFC 1883, the octet string MUST contain exactly sixteen octets [RFC
+     * 1883].
+     */
+    public GeneralName(
+        DERObject name, int tag)
+    {
+        this.obj = name;
+        this.tag = tag;
+    }
+
+    public DERObject getDERObject()
+    {
+        return new DERTaggedObject(false, tag, obj);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/GeneralNames.java b/src/org/bouncycastle/asn1/x509/GeneralNames.java
new file mode 100644 (file)
index 0000000..e13ffb0
--- /dev/null
@@ -0,0 +1,25 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.*;
+
+public class GeneralNames
+    implements DEREncodable
+{
+    DERConstructedSequence  seq;
+
+    public GeneralNames(
+        DERConstructedSequence  seq)
+    {
+        this.seq = seq;
+    }
+
+    /**
+     * <pre>
+     * GeneralNames ::= SEQUENCE SIZE {1..MAX} OF GeneralName
+     * </pre>
+     */
+    public DERObject getDERObject()
+    {
+        return seq;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/KeyUsage.java b/src/org/bouncycastle/asn1/x509/KeyUsage.java
new file mode 100644 (file)
index 0000000..e4ed284
--- /dev/null
@@ -0,0 +1,68 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.*;
+
+/**
+ * <pre>
+ *    id-ce-keyUsage OBJECT IDENTIFIER ::=  { id-ce 15 }
+ *
+ *    KeyUsage ::= BIT STRING {
+ *         digitalSignature        (0),
+ *         nonRepudiation          (1),
+ *         keyEncipherment         (2),
+ *         dataEncipherment        (3),
+ *         keyAgreement            (4),
+ *         keyCertSign             (5),
+ *         cRLSign                 (6),
+ *         encipherOnly            (7),
+ *         decipherOnly            (8) }
+ * </pre>
+ */
+public class KeyUsage
+    extends DERBitString
+{
+    public static final int        digitalSignature = (1 << 7); 
+    public static final int        nonRepudiation   = (1 << 6);
+    public static final int        keyEncipherment  = (1 << 5);
+    public static final int        dataEncipherment = (1 << 4);
+    public static final int        keyAgreement     = (1 << 3);
+    public static final int        keyCertSign      = (1 << 2);
+    public static final int        cRLSign          = (1 << 1);
+    public static final int        encipherOnly     = (1 << 0);
+    public static final int        decipherOnly     = (1 << 15);
+
+    static private byte[] getUsageBytes(
+        int usage)
+    {
+        byte[]  usageBytes = new byte[2];
+
+        usageBytes[0] = (byte)(usage & 0xFF);
+        usageBytes[1] = (byte)((usage >> 8) & 0xFF);
+
+        return usageBytes;
+    }
+
+    /**
+     * Basic constructor.
+     * 
+     * @param usage - the bitwise OR of the Key Usage flags giving the
+     * allowed uses for the key.
+     * e.g. (X509KeyUsage.keyEncipherment | X509KeyUsage.dataEncipherment)
+     */
+    public KeyUsage(
+        int usage)
+    {
+        super(getUsageBytes(usage), 7);
+    }
+
+    public KeyUsage(
+        DERBitString usage)
+    {
+        super(usage.getBytes(), usage.getPadBits());
+    }
+
+    public String toString()
+    {
+        return "KeyUsage: 0x" + Integer.toHexString(data[0] & 0xff);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/RSAPublicKeyStructure.java b/src/org/bouncycastle/asn1/x509/RSAPublicKeyStructure.java
new file mode 100644 (file)
index 0000000..b792fe1
--- /dev/null
@@ -0,0 +1,60 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Enumeration;
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.*;
+
+public class RSAPublicKeyStructure
+    implements DEREncodable
+{
+    private BigInteger  modulus;
+    private BigInteger  publicExponent;
+
+    public RSAPublicKeyStructure(
+        BigInteger  modulus,
+        BigInteger  publicExponent)
+    {
+        this.modulus = modulus;
+        this.publicExponent = publicExponent;
+    }
+
+    public RSAPublicKeyStructure(
+        DERConstructedSequence  seq)
+    {
+        Enumeration e = seq.getObjects();
+
+        modulus = ((DERInteger)e.nextElement()).getValue();
+        publicExponent = ((DERInteger)e.nextElement()).getValue();
+    }
+
+    public BigInteger getModulus()
+    {
+        return modulus;
+    }
+
+    public BigInteger getPublicExponent()
+    {
+        return publicExponent;
+    }
+
+    /**
+     * This outputs the key in PKCS1v2 format.
+     * <pre>
+     *      RSAPublicKey ::= SEQUENCE {
+     *                          modulus INTEGER, -- n
+     *                          publicExponent INTEGER, -- e
+     *                      }
+     * </pre>
+     * <p>
+     */
+    public DERObject getDERObject()
+    {
+        DERConstructedSequence  seq = new DERConstructedSequence();
+
+        seq.addObject(new DERInteger(getModulus()));
+        seq.addObject(new DERInteger(getPublicExponent()));
+
+        return seq;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/ReasonFlags.java b/src/org/bouncycastle/asn1/x509/ReasonFlags.java
new file mode 100644 (file)
index 0000000..568cf1f
--- /dev/null
@@ -0,0 +1,33 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.*;
+
+public class ReasonFlags
+    extends DERBitString
+{
+    public static final int KEY_COMPROMISE          = 1;
+    public static final int CA_COMPROMISE           = (1 << 2);
+    public static final int AFFILIATION_CHANGED     = (1 << 3);
+    public static final int SUPERSEDED              = (1 << 4);
+    public static final int CESSATION_OF_OPERATION  = (1 << 5);
+    public static final int CERTIFICATE_HOLD        = (1 << 6);
+
+    /**
+     * <pre>
+     * ReasonFlags ::= BIT STRING {
+     *    unused(0),
+     *    keyCompromise(1),
+     *    cACompromise(2),
+     *    affiliationChanged(3),
+     *    superseded(4),
+     *    cessationOfOperation(5),
+     *    certficateHold(6)
+     * }
+     * </pre>
+     */
+    public ReasonFlags(
+        int reasons)
+    {
+        super((byte)reasons, 1);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java b/src/org/bouncycastle/asn1/x509/SubjectKeyIdentifier.java
new file mode 100644 (file)
index 0000000..0dd2b27
--- /dev/null
@@ -0,0 +1,64 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.asn1.*;
+
+/**
+ * <pre>
+ * SubjectKeyIdentifier::= OCTET STRING
+ * </pre>
+ */
+public class SubjectKeyIdentifier
+    implements DEREncodable
+{
+       private byte[] keyidentifier;
+
+    public SubjectKeyIdentifier(
+        byte[] keyid)
+    {
+        this.keyidentifier=keyid;
+    }
+
+    public SubjectKeyIdentifier(
+        DEROctetString  keyid)
+    {
+               this.keyidentifier=keyid.getOctets();
+
+    }
+
+       /**
+        *
+        * Calulates the keyidentifier using a SHA1 hash over the BIT STRING
+        * from SubjectPublicKeyInfo as defined in RFC2459.
+        *
+        **/
+       public SubjectKeyIdentifier(
+               SubjectPublicKeyInfo    spki)
+       {
+               Digest  digest = new SHA1Digest();
+               byte[]  resBuf = new byte[digest.getDigestSize()];
+
+               DERBitString derpk = new DERBitString(spki.getPublicKey());
+               byte[] bytes = derpk.getBytes();
+               digest.update(bytes, 0, bytes.length);
+               digest.doFinal(resBuf, 0);
+               this.keyidentifier=resBuf;
+       }
+
+    public byte[] getKeyIdentifier()
+    {
+        return keyidentifier;
+    }
+
+     /**
+     * <pre>
+     * SubjectKeyIdentifier := OCTET STRING
+     * </pre>
+     */
+    public DERObject getDERObject()
+    {
+        DEROctetString oct = new DEROctetString(keyidentifier);
+        return oct;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java b/src/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java
new file mode 100644 (file)
index 0000000..2e65881
--- /dev/null
@@ -0,0 +1,78 @@
+package org.bouncycastle.asn1.x509;
+
+import java.io.*;
+import java.util.Enumeration;
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.*;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+
+/**
+ * The object that contains the public key stored in a certficate.
+ * <p>
+ * The getEncoded() method in the public keys in the JCE produces a DER
+ * encoded one of these.
+ */
+public class SubjectPublicKeyInfo
+    implements DEREncodable
+{
+    private AlgorithmIdentifier     algId;
+    private DERObject               pubKey;
+
+    public SubjectPublicKeyInfo(
+        AlgorithmIdentifier algId,
+        DERObject           publicKey)
+    {
+        this.pubKey = publicKey;
+        this.algId = algId;
+    }
+
+    public SubjectPublicKeyInfo(
+        DERConstructedSequence  seq)
+    {
+        Enumeration         e = seq.getObjects();
+
+        algId = new AlgorithmIdentifier((DERConstructedSequence)e.nextElement());
+
+        byte[]  keyData = ((DERBitString)e.nextElement()).getBytes();
+
+        try
+        {
+            ByteArrayInputStream    bIn = new ByteArrayInputStream(keyData);
+            DERInputStream          dIn = new DERInputStream(bIn);
+
+            pubKey = (DERObject)dIn.readObject();
+        }
+        catch (IOException ex)
+        {
+            throw new IllegalArgumentException("error recovering public key");
+        }
+    }
+
+    public AlgorithmIdentifier getAlgorithmId()
+    {
+        return algId;
+    }
+
+    public DERObject getPublicKey()
+    {
+        return pubKey;
+    }
+
+    /**
+     * <pre>
+     * SubjectPublicKeyInfo ::= SEQUENCE {
+     *                          algorithm AlgorithmIdentifier,
+     *                          publicKey BIT STRING }
+     * </pre>
+     */
+    public DERObject getDERObject()
+    {
+        DERConstructedSequence  seq = new DERConstructedSequence();
+
+        seq.addObject(algId);
+        seq.addObject(new DERBitString(pubKey));
+
+        return seq;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/TBSCertList.java b/src/org/bouncycastle/asn1/x509/TBSCertList.java
new file mode 100644 (file)
index 0000000..11cb2ab
--- /dev/null
@@ -0,0 +1,192 @@
+
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.*;
+import org.bouncycastle.asn1.pkcs.*;
+
+/**
+ * PKIX RFC-2459
+ *
+ * <pre>
+ * TBSCertList  ::=  SEQUENCE  {
+ *      version                 Version OPTIONAL,
+ *                                   -- if present, shall be v2
+ *      signature               AlgorithmIdentifier,
+ *      issuer                  Name,
+ *      thisUpdate              Time,
+ *      nextUpdate              Time OPTIONAL,
+ *      revokedCertificates     SEQUENCE OF SEQUENCE  {
+ *           userCertificate         CertificateSerialNumber,
+ *           revocationDate          Time,
+ *           crlEntryExtensions      Extensions OPTIONAL
+ *                                         -- if present, shall be v2
+ *                                }  OPTIONAL,
+ *      crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
+ *                                         -- if present, shall be v2
+ *                                }
+ * </pre>
+ */
+
+public class TBSCertList
+       implements DEREncodable
+{
+       public class CRLEntry
+               implements DEREncodable
+       {
+               DERConstructedSequence  seq;
+
+               DERInteger              userCertificate;
+               DERUTCTime              revocationDate;
+               X509Extensions  crlEntryExtensions;
+
+               public CRLEntry(
+                       DERConstructedSequence  seq)
+               {
+                       this.seq = seq;
+
+                       userCertificate = (DERInteger)seq.getObjectAt(0);
+                       revocationDate = (DERUTCTime)seq.getObjectAt(1);
+                       if ( seq.getSize() == 3 )
+                       {
+                               crlEntryExtensions = new X509Extensions((DERConstructedSequence)seq.getObjectAt(2));
+                       }
+               }
+
+               public DERInteger getUserCertificate()
+               {
+                       return userCertificate;
+               }
+
+               public DERUTCTime getRevocationDate()
+               {
+                       return revocationDate;
+               }
+
+               public X509Extensions getExtensions()
+               {
+                       return crlEntryExtensions;
+               }
+
+               public DERObject getDERObject()
+               {
+                       return seq;
+               }
+       }
+
+    DERConstructedSequence  seq;
+
+    DERInteger              version;
+    AlgorithmIdentifier     signature;
+    X509Name                issuer;
+       DERUTCTime                              thisUpdate;
+       DERUTCTime                              nextUpdate;
+       CRLEntry[]                              revokedCertificates;
+    X509Extensions          crlExtensions;
+
+    public TBSCertList(
+        DERConstructedSequence  seq)
+    {
+        int seqPos = 0;
+
+        this.seq = seq;
+
+        if ( seq.getObjectAt(seqPos) instanceof DERInteger )
+        {
+            version = (DERInteger)seq.getObjectAt(seqPos++);
+        }
+        else
+        {
+            version = new DERInteger(0);
+        }
+
+        if ( seq.getObjectAt(seqPos) instanceof AlgorithmIdentifier )
+        {
+            signature = (AlgorithmIdentifier)seq.getObjectAt(seqPos++);
+        }
+        else
+        {
+            signature = new AlgorithmIdentifier((DERConstructedSequence)seq.getObjectAt(seqPos++));
+        }
+
+        if ( seq.getObjectAt(seqPos) instanceof X509Name )
+        {
+            issuer = (X509Name)seq.getObjectAt(seqPos++);
+        }
+        else
+        {
+            issuer = new X509Name((DERConstructedSequence)seq.getObjectAt(seqPos++));
+        }
+
+        thisUpdate = (DERUTCTime)seq.getObjectAt(seqPos++);
+
+               if ( seqPos < seq.getSize()
+               && seq.getObjectAt(seqPos) instanceof DERUTCTime )
+        {
+                       nextUpdate = (DERUTCTime)seq.getObjectAt(seqPos++);
+        }
+
+               if ( seqPos < seq.getSize()
+                       && !(seq.getObjectAt(seqPos) instanceof DERTaggedObject) )
+               {
+                       DERConstructedSequence certs = (DERConstructedSequence)seq.getObjectAt(seqPos++);
+                       revokedCertificates = new CRLEntry[certs.getSize()];
+
+                       for ( int i = 0; i < revokedCertificates.length; i++ )
+                       {
+                               revokedCertificates[i] = new CRLEntry((DERConstructedSequence)certs.getObjectAt(i));
+                       }
+               }
+
+               if ( seqPos < seq.getSize()
+                       && seq.getObjectAt(seqPos) instanceof DERTaggedObject )
+               {
+                       crlExtensions = new X509Extensions((DERConstructedSequence)((DERTaggedObject)seq.getObjectAt(seqPos++)).getObject());
+               }
+    }
+
+    public int getVersion()
+    {
+        return version.getValue().intValue() + 1;
+    }
+
+    public DERInteger getVersionNumber()
+    {
+        return version;
+    }
+
+    public AlgorithmIdentifier getSignature()
+    {
+        return signature;
+    }
+
+    public X509Name getIssuer()
+    {
+        return issuer;
+    }
+
+    public DERUTCTime getThisUpdate()
+    {
+        return thisUpdate;
+    }
+
+    public DERUTCTime getNextUpdate()
+    {
+        return nextUpdate;
+    }
+
+    public CRLEntry[] getRevokedCertificates()
+    {
+        return revokedCertificates;
+    }
+
+    public X509Extensions getExtensions()
+    {
+        return crlExtensions;
+    }
+
+    public DERObject getDERObject()
+    {
+        return seq;
+    }
+}
+
diff --git a/src/org/bouncycastle/asn1/x509/TBSCertificateStructure.java b/src/org/bouncycastle/asn1/x509/TBSCertificateStructure.java
new file mode 100644 (file)
index 0000000..d5c7cf7
--- /dev/null
@@ -0,0 +1,191 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.*;
+import org.bouncycastle.asn1.pkcs.*;
+
+/**
+ * <pre>
+ * TBSCertificate ::= SEQUENCE {
+ *      version          [ 0 ]  Version DEFAULT v1(0),
+ *      serialNumber            CertificateSerialNumber,
+ *      signature               AlgorithmIdentifier,
+ *      issuer                  Name,
+ *      validity                Validity,
+ *      subject                 Name,
+ *      subjectPublicKeyInfo    SubjectPublicKeyInfo,
+ *      issuerUniqueID    [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ *      subjectUniqueID   [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ *      extensions        [ 3 ] Extensions OPTIONAL
+ *      }
+ * </pre>
+ * <p>
+ * Note: issuerUniqueID and subjectUniqueID are both deprecated by the IETF. This class
+ * will parse them, but you really shouldn't be creating new ones.
+ */
+public class TBSCertificateStructure
+    implements DEREncodable, X509ObjectIdentifiers, PKCSObjectIdentifiers
+{
+    DERConstructedSequence  seq;
+
+    DERInteger              version;
+    DERInteger              serialNumber;
+    AlgorithmIdentifier     signature;
+    X509Name                issuer;
+    DERUTCTime              startDate, endDate;
+    X509Name                subject;
+    SubjectPublicKeyInfo    subjectPublicKeyInfo;
+    DERBitString            issuerUniqueId;
+    DERBitString            subjectUniqueId;
+    X509Extensions          extensions;
+
+    public TBSCertificateStructure(
+        DERConstructedSequence  seq)
+    {
+        int         seqStart = 0;
+
+        this.seq = seq;
+
+        //
+        // some certficates don't include a version number - we assume v1
+        //
+        if (seq.getObjectAt(0) instanceof DERTaggedObject)
+        {
+            version = (DERInteger)((DERTaggedObject)seq.getObjectAt(0)).getObject();
+        }
+        else
+        {
+            seqStart = -1;          // field 0 is missing!
+            version = new DERInteger(0);
+        }
+
+        serialNumber = (DERInteger)seq.getObjectAt(seqStart + 1);
+
+        if (seq.getObjectAt(seqStart + 2) instanceof AlgorithmIdentifier)
+        {
+            signature = (AlgorithmIdentifier)seq.getObjectAt(seqStart + 2);
+        }
+        else
+        {
+            signature = new AlgorithmIdentifier((DERConstructedSequence)seq.getObjectAt(seqStart + 2));
+        }
+
+        if (seq.getObjectAt(seqStart + 3) instanceof X509Name)
+        {
+            issuer = (X509Name)seq.getObjectAt(seqStart + 3);
+        }
+        else
+        {
+            issuer = new X509Name((DERConstructedSequence)seq.getObjectAt(seqStart + 3));
+        }
+
+        //
+        // before and after dates
+        //
+        DERConstructedSequence  dates = (DERConstructedSequence)seq.getObjectAt(seqStart + 4);
+        startDate = (DERUTCTime)dates.getObjectAt(0);
+        endDate = (DERUTCTime)dates.getObjectAt(1);
+
+        if (seq.getObjectAt(seqStart + 5) instanceof X509Name)
+        {
+            subject = (X509Name)seq.getObjectAt(seqStart + 5);
+        }
+        else
+        {
+            subject = new X509Name((DERConstructedSequence)seq.getObjectAt(seqStart + 5));
+        }
+
+        //
+        // public key info.
+        //
+        if (seq.getObjectAt(seqStart + 6) instanceof SubjectPublicKeyInfo)
+        {
+            subjectPublicKeyInfo = (SubjectPublicKeyInfo)seq.getObjectAt(seqStart + 6);
+        }
+        else
+        {
+            subjectPublicKeyInfo = new SubjectPublicKeyInfo((DERConstructedSequence)seq.getObjectAt(seqStart + 6));
+        }
+
+        for (int extras = seq.getSize() - (seqStart + 6) - 1; extras > 0; extras--)
+        {
+            DERTaggedObject extra = (DERTaggedObject)seq.getObjectAt(seqStart + 6 + extras);
+
+            switch (extra.getTagNo())
+            {
+            case 1:
+                issuerUniqueId = (DERBitString)extra.getObject();
+                break;
+            case 2:
+                subjectUniqueId = (DERBitString)extra.getObject();
+                break;
+            case 3:
+                extensions = new X509Extensions((DERConstructedSequence)extra.getObject());
+            }
+        }
+    }
+
+    public int getVersion()
+    {
+        return version.getValue().intValue() + 1;
+    }
+
+    public DERInteger getVersionNumber()
+    {
+        return version;
+    }
+
+    public DERInteger getSerialNumber()
+    {
+        return serialNumber;
+    }
+
+    public AlgorithmIdentifier getSignature()
+    {
+        return signature;
+    }
+
+    public X509Name getIssuer()
+    {
+        return issuer;
+    }
+
+    public DERUTCTime getStartDate()
+    {
+        return startDate;
+    }
+
+    public DERUTCTime getEndDate()
+    {
+        return endDate;
+    }
+
+    public X509Name getSubject()
+    {
+        return subject;
+    }
+
+    public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+    {
+        return subjectPublicKeyInfo;
+    }
+
+    public DERBitString getIssuerUniqueId()
+    {
+        return issuerUniqueId;
+    }
+
+    public DERBitString getSubjectUniqueId()
+    {
+        return subjectUniqueId;
+    }
+
+    public X509Extensions getExtensions()
+    {
+        return extensions;
+    }
+
+    public DERObject getDERObject()
+    {
+        return seq;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java b/src/org/bouncycastle/asn1/x509/V1TBSCertificateGenerator.java
new file mode 100644 (file)
index 0000000..9cb5745
--- /dev/null
@@ -0,0 +1,110 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.*;
+import org.bouncycastle.asn1.pkcs.*;
+
+/**
+ * Generator for Version 1 TBSCertificateStructures.
+ * <pre>
+ * TBSCertificate ::= SEQUENCE {
+ *      version          [ 0 ]  Version DEFAULT v1(0),
+ *      serialNumber            CertificateSerialNumber,
+ *      signature               AlgorithmIdentifier,
+ *      issuer                  Name,
+ *      validity                Validity,
+ *      subject                 Name,
+ *      subjectPublicKeyInfo    SubjectPublicKeyInfo,
+ *      }
+ * </pre>
+ *
+ */
+public class V1TBSCertificateGenerator
+{
+    DERTaggedObject         version = new DERTaggedObject(0, new DERInteger(0));
+
+    DERInteger              serialNumber;
+    AlgorithmIdentifier     signature;
+    X509Name                issuer;
+    DERUTCTime              startDate, endDate;
+    X509Name                subject;
+    SubjectPublicKeyInfo    subjectPublicKeyInfo;
+
+    public V1TBSCertificateGenerator()
+    {
+    }
+
+    public void setSerialNumber(
+        DERInteger  serialNumber)
+    {
+        this.serialNumber = serialNumber;
+    }
+
+    public void setSignature(
+        AlgorithmIdentifier    signature)
+    {
+        this.signature = signature;
+    }
+
+    public void setIssuer(
+        X509Name    issuer)
+    {
+        this.issuer = issuer;
+    }
+
+    public void setStartDate(
+        DERUTCTime startDate)
+    {
+        this.startDate = startDate;
+    }
+
+    public void setEndDate(
+        DERUTCTime endDate)
+    {
+        this.endDate = endDate;
+    }
+
+    public void setSubject(
+        X509Name    subject)
+    {
+        this.subject = subject;
+    }
+
+    public void setSubjectPublicKeyInfo(
+        SubjectPublicKeyInfo    pubKeyInfo)
+    {
+        this.subjectPublicKeyInfo = pubKeyInfo;
+    }
+
+    public TBSCertificateStructure generateTBSCertificate()
+    {
+        if ((serialNumber == null) || (signature == null)
+            || (issuer == null) || (startDate == null) || (endDate == null)
+            || (subject == null) || (subjectPublicKeyInfo == null))
+        {
+            throw new IllegalStateException("not all mandatory fields set in V1 TBScertificate generator");
+        }
+
+        DERConstructedSequence  seq = new DERConstructedSequence();
+
+        seq.addObject(version);
+        seq.addObject(serialNumber);
+        seq.addObject(signature);
+        seq.addObject(issuer);
+
+        //
+        // before and after dates
+        //
+        DERConstructedSequence  validity = new DERConstructedSequence();
+
+        validity.addObject(startDate);
+        validity.addObject(endDate);
+
+        seq.addObject(validity);
+
+        seq.addObject(subject);
+
+        seq.addObject(subjectPublicKeyInfo);
+
+        return new TBSCertificateStructure(seq);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/V2TBSCertListGenerator.java b/src/org/bouncycastle/asn1/x509/V2TBSCertListGenerator.java
new file mode 100644 (file)
index 0000000..92b82a3
--- /dev/null
@@ -0,0 +1,138 @@
+package org.bouncycastle.asn1.x509;
+
+import java.util.Vector;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.*;
+
+/**
+ * Generator for Version 2 TBSCertList structures.
+ * <pre>
+ *  TBSCertList  ::=  SEQUENCE  {
+ *       version                 Version OPTIONAL,
+ *                                    -- if present, shall be v2
+ *       signature               AlgorithmIdentifier,
+ *       issuer                  Name,
+ *       thisUpdate              Time,
+ *       nextUpdate              Time OPTIONAL,
+ *       revokedCertificates     SEQUENCE OF SEQUENCE  {
+ *            userCertificate         CertificateSerialNumber,
+ *            revocationDate          Time,
+ *            crlEntryExtensions      Extensions OPTIONAL
+ *                                          -- if present, shall be v2
+ *                                 }  OPTIONAL,
+ *       crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
+ *                                          -- if present, shall be v2
+ *                                 }
+ * </pre>
+ *
+ * <b>Note: This class may be subject to change</b>
+ */
+public class V2TBSCertListGenerator
+{
+    DERInteger version = new DERInteger(1);
+
+    AlgorithmIdentifier     signature;
+    X509Name                issuer;
+    DERUTCTime              thisUpdate, nextUpdate=null;
+    X509Extensions          extensions=null;
+    private Vector          crlentries=null;
+
+    public V2TBSCertListGenerator()
+    {
+    }
+
+
+    public void setSignature(
+        AlgorithmIdentifier    signature)
+    {
+        this.signature = signature;
+    }
+
+    public void setIssuer(
+        X509Name    issuer)
+    {
+        this.issuer = issuer;
+    }
+
+    public void setThisUpdate(
+        DERUTCTime thisUpdate)
+    {
+        this.thisUpdate = thisUpdate;
+    }
+
+    public void setNextUpdate(
+        DERUTCTime nextUpdate)
+    {
+        this.nextUpdate = nextUpdate;
+    }
+
+
+    public void addCRLEntry(
+        DERConstructedSequence crlEntry)
+    {
+        if (crlentries == null)
+            crlentries = new Vector();
+        crlentries.addElement(crlEntry);
+    }
+
+    public void addCRLEntry(DERInteger userCertificate, DERUTCTime revocationDate, int reason)
+    {
+        DERConstructedSequence seq = new DERConstructedSequence();
+        seq.addObject(userCertificate);
+        seq.addObject(revocationDate);
+        if (reason != 0)
+        {
+            ReasonFlags rf = new ReasonFlags(reason);
+            DERConstructedSequence eseq = new DERConstructedSequence();
+            eseq.addObject(X509Extensions.ReasonCode);
+            eseq.addObject(rf);
+            X509Extensions ex = new X509Extensions(eseq);
+            seq.addObject(ex);
+        }
+        if (crlentries == null)
+            crlentries = new Vector();
+        crlentries.addElement(seq);
+    }
+
+    public void setExtensions(
+        X509Extensions    extensions)
+    {
+        this.extensions = extensions;
+    }
+
+    public TBSCertList generateTBSCertList()
+    {
+        if ((signature == null) || (issuer == null) || (thisUpdate == null))
+        {
+            throw new IllegalStateException("Not all mandatory fields set in V2 TBSCertList generator.");
+        }
+
+        DERConstructedSequence  seq = new DERConstructedSequence();
+
+        seq.addObject(version);
+        seq.addObject(signature);
+        seq.addObject(issuer);
+
+        seq.addObject(thisUpdate);
+        if (nextUpdate != null)
+            seq.addObject(nextUpdate);
+
+        // Add CRLEntries if they exist
+        if (crlentries != null) {
+            DERConstructedSequence certseq = new DERConstructedSequence();
+            Enumeration it = crlentries.elements();
+            while( it.hasMoreElements() ) {
+                certseq.addObject((DERConstructedSequence)it.nextElement());
+            }
+            seq.addObject(certseq);
+        }
+
+        if (extensions != null)
+        {
+            seq.addObject(new DERTaggedObject(0, extensions.getDERObject()));
+        }
+
+        return new TBSCertList(seq);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java b/src/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator.java
new file mode 100644 (file)
index 0000000..ae966ec
--- /dev/null
@@ -0,0 +1,125 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.*;
+import org.bouncycastle.asn1.pkcs.*;
+
+/**
+ * Generator for Version 3 TBSCertificateStructures.
+ * <pre>
+ * TBSCertificate ::= SEQUENCE {
+ *      version          [ 0 ]  Version DEFAULT v1(0),
+ *      serialNumber            CertificateSerialNumber,
+ *      signature               AlgorithmIdentifier,
+ *      issuer                  Name,
+ *      validity                Validity,
+ *      subject                 Name,
+ *      subjectPublicKeyInfo    SubjectPublicKeyInfo,
+ *      issuerUniqueID    [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ *      subjectUniqueID   [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ *      extensions        [ 3 ] Extensions OPTIONAL
+ *      }
+ * </pre>
+ *
+ */
+public class V3TBSCertificateGenerator
+{
+    DERTaggedObject         version = new DERTaggedObject(0, new DERInteger(2));
+
+    DERInteger              serialNumber;
+    AlgorithmIdentifier     signature;
+    X509Name                issuer;
+    DERUTCTime              startDate, endDate;
+    X509Name                subject;
+    SubjectPublicKeyInfo    subjectPublicKeyInfo;
+    X509Extensions          extensions;
+
+    public V3TBSCertificateGenerator()
+    {
+    }
+
+    public void setSerialNumber(
+        DERInteger  serialNumber)
+    {
+        this.serialNumber = serialNumber;
+    }
+
+    public void setSignature(
+        AlgorithmIdentifier    signature)
+    {
+        this.signature = signature;
+    }
+
+    public void setIssuer(
+        X509Name    issuer)
+    {
+        this.issuer = issuer;
+    }
+
+    public void setStartDate(
+        DERUTCTime startDate)
+    {
+        this.startDate = startDate;
+    }
+
+    public void setEndDate(
+        DERUTCTime endDate)
+    {
+        this.endDate = endDate;
+    }
+
+    public void setSubject(
+        X509Name    subject)
+    {
+        this.subject = subject;
+    }
+
+    public void setSubjectPublicKeyInfo(
+        SubjectPublicKeyInfo    pubKeyInfo)
+    {
+        this.subjectPublicKeyInfo = pubKeyInfo;
+    }
+
+    public void setExtensions(
+        X509Extensions    extensions)
+    {
+        this.extensions = extensions;
+    }
+
+    public TBSCertificateStructure generateTBSCertificate()
+    {
+        if ((serialNumber == null) || (signature == null)
+            || (issuer == null) || (startDate == null) || (endDate == null)
+            || (subject == null) || (subjectPublicKeyInfo == null))
+        {
+            throw new IllegalStateException("not all mandatory fields set in V3 TBScertificate generator");
+        }
+
+        DERConstructedSequence  seq = new DERConstructedSequence();
+
+        seq.addObject(version);
+        seq.addObject(serialNumber);
+        seq.addObject(signature);
+        seq.addObject(issuer);
+
+        //
+        // before and after dates
+        //
+        DERConstructedSequence  validity = new DERConstructedSequence();
+
+        validity.addObject(startDate);
+        validity.addObject(endDate);
+
+        seq.addObject(validity);
+
+        seq.addObject(subject);
+
+        seq.addObject(subjectPublicKeyInfo);
+
+        if (extensions != null)
+        {
+            seq.addObject(new DERTaggedObject(3, extensions.getDERObject()));
+        }
+
+        return new TBSCertificateStructure(seq);
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/X509CertificateStructure.java b/src/org/bouncycastle/asn1/x509/X509CertificateStructure.java
new file mode 100644 (file)
index 0000000..68479c5
--- /dev/null
@@ -0,0 +1,110 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.*;
+import org.bouncycastle.asn1.pkcs.*;
+
+/**
+ * an X509Certificate structure.
+ * <pre>
+ *  Certificate ::= SEQUENCE {
+ *      tbsCertificate          TBSCertificate,
+ *      signatureAlgorithm      AlgorithmIdentifier,
+ *      signature               BIT STRING
+ *  }
+ * </pre>
+ */
+public class X509CertificateStructure
+    implements DEREncodable, X509ObjectIdentifiers, PKCSObjectIdentifiers
+{
+    DERConstructedSequence  seq;
+    TBSCertificateStructure tbsCert;
+    AlgorithmIdentifier     sigAlgId;
+    DERBitString            sig;
+
+    public X509CertificateStructure(
+        DERConstructedSequence  seq)
+    {
+        this.seq = seq;
+
+        //
+        // correct x509 certficate
+        //
+        if (seq.getSize() == 3)
+        {
+            if (seq.getObjectAt(0) instanceof TBSCertificateStructure)
+            {
+                tbsCert = (TBSCertificateStructure)seq.getObjectAt(0);
+            }
+            else
+            {
+                tbsCert = new TBSCertificateStructure((DERConstructedSequence)seq.getObjectAt(0));
+            }
+
+            if (seq.getObjectAt(1) instanceof AlgorithmIdentifier)
+            {
+                sigAlgId = (AlgorithmIdentifier)seq.getObjectAt(1);
+            }
+            else
+            {
+                sigAlgId = new AlgorithmIdentifier((DERConstructedSequence)seq.getObjectAt(1));
+            }
+
+            sig = (DERBitString)seq.getObjectAt(2);
+        }
+    }
+
+    public TBSCertificateStructure getTBSCertificate()
+    {
+        return tbsCert;
+    }
+
+    public int getVersion()
+    {
+        return tbsCert.getVersion();
+    }
+
+    public DERInteger getSerialNumber()
+    {
+        return tbsCert.getSerialNumber();
+    }
+
+    public X509Name getIssuer()
+    {
+        return tbsCert.getIssuer();
+    }
+
+    public DERUTCTime getStartDate()
+    {
+        return tbsCert.getStartDate();
+    }
+
+    public DERUTCTime getEndDate()
+    {
+        return tbsCert.getEndDate();
+    }
+
+    public X509Name getSubject()
+    {
+        return tbsCert.getSubject();
+    }
+
+    public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+    {
+        return tbsCert.getSubjectPublicKeyInfo();
+    }
+
+    public AlgorithmIdentifier getSignatureAlgorithm()
+    {
+        return sigAlgId;
+    }
+
+    public DERBitString getSignature()
+    {
+        return sig;
+    }
+
+    public DERObject getDERObject()
+    {
+        return seq;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/X509Extension.java b/src/org/bouncycastle/asn1/x509/X509Extension.java
new file mode 100644 (file)
index 0000000..f2fb1b9
--- /dev/null
@@ -0,0 +1,63 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.*;
+
+/**
+ * an object for the elements in the X.509 V3 extension block.
+ */
+public class X509Extension
+{
+    boolean             critical;
+    DEROctetString      value;
+
+    public X509Extension(
+        DERBoolean              critical,
+        DEROctetString          value)
+    {
+        this.critical = critical.isTrue();
+        this.value = value;
+    }
+
+    public X509Extension(
+        boolean                 critical,
+        DEROctetString          value)
+    {
+        this.critical = critical;
+        this.value = value;
+    }
+
+    public boolean isCritical()
+    {
+        return critical;
+    }
+
+    public DEROctetString getValue()
+    {
+        return value;
+    }
+
+    public int hashCode()
+    {
+        if (this.isCritical())
+        {
+            return this.getValue().hashCode();
+        }
+
+        
+        return ~this.getValue().hashCode();
+    }
+
+    public boolean equals(
+        Object  o)
+    {
+        if (o == null || !(o instanceof X509Extension))
+        {
+            return false;
+        }
+
+        X509Extension   other = (X509Extension)o;
+
+        return other.getValue().equals(this.getValue())
+            && (other.isCritical() == this.isCritical());
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/X509Extensions.java b/src/org/bouncycastle/asn1/x509/X509Extensions.java
new file mode 100644 (file)
index 0000000..7fb75f6
--- /dev/null
@@ -0,0 +1,267 @@
+package org.bouncycastle.asn1.x509;
+
+import java.io.*;
+import java.util.*;
+
+import org.bouncycastle.asn1.*;
+
+public class X509Extensions
+    implements DEREncodable
+{
+    /**
+     * Subject Key Identifier 
+     */
+    public static final DERObjectIdentifier SubjectKeyIdentifier = new DERObjectIdentifier("2.5.29.14");
+
+    /**
+     * Key Usage 
+     */
+    public static final DERObjectIdentifier KeyUsage = new DERObjectIdentifier("2.5.29.15");
+
+    /**
+     * Private Key Usage Period 
+     */
+    public static final DERObjectIdentifier PrivateKeyUsagePeriod = new DERObjectIdentifier("2.5.29.16");
+
+    /**
+     * Subject Alternative Name 
+     */
+    public static final DERObjectIdentifier SubjectAlternativeName = new DERObjectIdentifier("2.5.29.17");
+
+    /**
+     * Issuer Alternative Name 
+     */
+    public static final DERObjectIdentifier IssuerAlternativeName = new DERObjectIdentifier("2.5.29.18");
+
+    /**
+     * Basic Constraints 
+     */
+    public static final DERObjectIdentifier BasicConstraints = new DERObjectIdentifier("2.5.29.19");
+
+    /**
+     * CRL Number 
+     */
+    public static final DERObjectIdentifier CRLNumber = new DERObjectIdentifier("2.5.29.20");
+
+    /**
+     * Reason code 
+     */
+    public static final DERObjectIdentifier ReasonCode = new DERObjectIdentifier("2.5.29.21");
+
+    /**
+     * Hold Instruction Code 
+     */
+    public static final DERObjectIdentifier InstructionCode = new DERObjectIdentifier("2.5.29.23");
+
+    /**
+     * Invalidity Date 
+     */
+    public static final DERObjectIdentifier InvalidityDate = new DERObjectIdentifier("2.5.29.24");
+
+    /**
+     * Delta CRL indicator 
+     */
+    public static final DERObjectIdentifier DeltaCRLIndicator = new DERObjectIdentifier("2.5.29.27");
+
+    /**
+     * Issuing Distribution Point 
+     */
+    public static final DERObjectIdentifier IssuingDistributionPoint = new DERObjectIdentifier("2.5.29.28");
+
+    /**
+     * Certificate Issuer 
+     */
+    public static final DERObjectIdentifier CertificateIssuer = new DERObjectIdentifier("2.5.29.29");
+
+    /**
+     * Name Constraints 
+     */
+    public static final DERObjectIdentifier NameConstraints = new DERObjectIdentifier("2.5.29.30");
+
+    /**
+     * CRL Distribution Points 
+     */
+    public static final DERObjectIdentifier CRLDistributionPoints = new DERObjectIdentifier("2.5.29.31");
+
+    /**
+     * Certificate Policies 
+     */
+    public static final DERObjectIdentifier CertificatePolicies = new DERObjectIdentifier("2.5.29.32");
+
+    /**
+     * Policy Mappings 
+     */
+    public static final DERObjectIdentifier PolicyMappings = new DERObjectIdentifier("2.5.29.33");
+
+    /**
+     * Authority Key Identifier 
+     */
+    public static final DERObjectIdentifier AuthorityKeyIdentifier = new DERObjectIdentifier("2.5.29.35");
+
+    /**
+     * Policy Constraints 
+     */
+    public static final DERObjectIdentifier PolicyConstraints = new DERObjectIdentifier("2.5.29.36");
+
+    private Hashtable               extensions = new Hashtable();
+    private Vector                  ordering = new Vector();
+    private DERConstructedSequence  seq;
+
+    /**
+     * Constructor from DERConstructedSequence.
+     *
+     * the extensions are a list of constructed sequences, either with (OID, OctetString) or (OID, Boolean, OctetString)
+     */
+    public X509Extensions(
+        DERConstructedSequence  seq)
+    {
+        this.seq = seq; 
+
+        Enumeration e = seq.getObjects();
+
+        while (e.hasMoreElements())
+        {
+            DERConstructedSequence  s = (DERConstructedSequence)e.nextElement();
+            Enumeration             e1 = s.getObjects();
+
+            if (s.getSize() == 3)
+            {
+                extensions.put(s.getObjectAt(0), new X509Extension((DERBoolean)s.getObjectAt(1), (DEROctetString)s.getObjectAt(2)));
+            }
+            else
+            {
+                extensions.put(s.getObjectAt(0), new X509Extension(false, (DEROctetString)s.getObjectAt(1)));
+            }
+
+            ordering.addElement(s.getObjectAt(0));
+        }
+    }
+
+    /**
+     * constructor from a table of extensions.
+     * <p>
+     * it's is assumed the table contains OID/String pairs.
+     */
+    public X509Extensions(
+        Hashtable  extensions)
+    {
+        this(null, extensions);
+    }
+
+    /**
+     * constructor from a table of extensions with ordering
+     * <p>
+     * it's is assumed the table contains OID/String pairs.
+     */
+    public X509Extensions(
+        Vector      ordering,
+        Hashtable   extensions)
+    {
+        this.seq = new DERConstructedSequence(); 
+
+        if (ordering == null)
+        {
+            Enumeration e = extensions.keys();
+
+            ordering = this.ordering;
+
+            while (e.hasMoreElements())
+            {
+                this.ordering.addElement(e.nextElement()); 
+            }
+        }
+
+        Enumeration     e = ordering.elements();
+
+        while (e.hasMoreElements())
+        {
+            DERObjectIdentifier     oid = (DERObjectIdentifier)e.nextElement();
+            X509Extension           ext = (X509Extension)extensions.get(oid);
+            DERConstructedSequence  s = new DERConstructedSequence();
+
+            s.addObject(oid);
+
+            if (ext.isCritical())
+            {
+                s.addObject(new DERBoolean(true));
+            }
+
+            s.addObject(ext.getValue());
+
+            seq.addObject(s);
+        }
+    }
+
+    /**
+     * return an Enumeration of the extension field's object ids.
+     */
+    public Enumeration oids()
+    {
+        return ordering.elements();
+    }
+
+    /**
+     * return the extension represented by the object identifier
+     * passed in.
+     *
+     * @return the extension if it's present, null otherwise.
+     */
+    public X509Extension getExtension(
+        DERObjectIdentifier oid)
+    {
+        return (X509Extension)extensions.get(oid);
+    }
+
+    public DERObject getDERObject()
+    {
+        return seq;
+    }
+
+    public int hashCode()
+    {
+        Enumeration     e = extensions.keys();
+        int             hashCode = 0;
+
+        while (e.hasMoreElements())
+        {
+            Object  o = e.nextElement();
+
+            hashCode ^= o.hashCode();
+            hashCode ^= extensions.get(o).hashCode();
+        }
+
+        return hashCode;
+    }
+
+    public boolean equals(
+        Object o)
+    {
+        if (o == null || !(o instanceof X509Extensions))
+        {
+            return false;
+        }
+
+        X509Extensions  other = (X509Extensions)o;
+
+        Enumeration     e1 = extensions.keys();
+        Enumeration     e2 = other.extensions.keys();
+
+        while (e1.hasMoreElements() && e2.hasMoreElements())
+        {
+            Object  o1 = e1.nextElement();
+            Object  o2 = e2.nextElement();
+            
+            if (!o1.equals(o2))
+            {
+                return false;
+            }
+        }
+
+        if (e1.hasMoreElements() || e2.hasMoreElements())
+        {
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/X509Name.java b/src/org/bouncycastle/asn1/x509/X509Name.java
new file mode 100644 (file)
index 0000000..2f2a60f
--- /dev/null
@@ -0,0 +1,336 @@
+package org.bouncycastle.asn1.x509;
+
+import java.io.*;
+import java.util.*;
+
+import org.bouncycastle.asn1.*;
+
+public class X509Name
+    implements DEREncodable
+{
+    /**
+     * country code - StringType(SIZE(2))
+     */
+    public static final DERObjectIdentifier C = new DERObjectIdentifier("2.5.4.6");
+
+    /**
+     * organization - StringType(SIZE(1..64))
+     */
+    public static final DERObjectIdentifier O = new DERObjectIdentifier("2.5.4.10");
+
+    /**
+     * organizational unit name - StringType(SIZE(1..64))
+     */
+    public static final DERObjectIdentifier OU = new DERObjectIdentifier("2.5.4.11");
+
+    /**
+     * common name - StringType(SIZE(1..64))
+     */
+    public static final DERObjectIdentifier CN = new DERObjectIdentifier("2.5.4.3");
+
+    /**
+     * device serial number name - StringType(SIZE(1..64))
+     */
+    public static final DERObjectIdentifier SN = new DERObjectIdentifier("2.5.4.5");
+
+    /**
+     * locality name - StringType(SIZE(1..64))
+     */
+    public static final DERObjectIdentifier L = new DERObjectIdentifier("2.5.4.7");
+
+    /**
+     * state, or province name - StringType(SIZE(1..64))
+     */
+    public static final DERObjectIdentifier ST = new DERObjectIdentifier("2.5.4.8");
+
+    /**
+     * email address (RSA PKCS#9 extension) - IA5String
+     * <p>
+     * note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
+     */
+    public static final DERObjectIdentifier EmailAddress = new DERObjectIdentifier("1.2.840.113549.1.9.1");
+
+    /**
+     * look up table translating OID values into their common symbols.
+     */
+    public static Hashtable OIDLookUp = new Hashtable();
+
+    /**
+     * look up table translating common symbols into their OIDS.
+     */
+    public static Hashtable SymbolLookUp = new Hashtable();
+
+    static
+    {
+        OIDLookUp.put(C, "C");
+        OIDLookUp.put(O, "O");
+        OIDLookUp.put(OU, "OU");
+        OIDLookUp.put(CN, "CN");
+        OIDLookUp.put(L, "L");
+        OIDLookUp.put(ST, "ST");
+        OIDLookUp.put(SN, "SN");
+        OIDLookUp.put(EmailAddress, "EmailAddress");
+
+        SymbolLookUp.put("C", C);
+        SymbolLookUp.put("O", O);
+        SymbolLookUp.put("OU", OU);
+        SymbolLookUp.put("CN", CN);
+        SymbolLookUp.put("L", L);
+        SymbolLookUp.put("ST", ST);
+        SymbolLookUp.put("SN", SN);
+        SymbolLookUp.put("EmailAddress", EmailAddress);
+    }
+
+    private Vector                  ordering = new Vector();
+    private Hashtable               attributes = new Hashtable();
+    private DERConstructedSequence  seq = null;
+
+    /**
+     * Constructor from DERConstructedSequence.
+     *
+     * the principal will be a list of constructed sets, each containing an (OID, String) pair.
+     */
+    public X509Name(
+        DERConstructedSequence  seq)
+    {
+        this.seq = seq; 
+
+        Enumeration e = seq.getObjects();
+
+        while (e.hasMoreElements())
+        {
+            DERSet  set = (DERSet)e.nextElement();
+            DERConstructedSequence  s = (DERConstructedSequence)set.getSequence();
+
+            ordering.addElement(s.getObjectAt(0));
+            attributes.put(s.getObjectAt(0), ((DERString)s.getObjectAt(1)).getString());
+        }
+    }
+
+    /**
+     * constructor from a table of attributes.
+     * <p>
+     * it's is assumed the table contains OID/String pairs, and the contents
+     * of the table are copied into an internal table as part of the 
+     * construction process.
+     * <p>
+     * <b>Note:</b> if the name you are trying to generate should be
+     * following a specific ordering, you should use the constructor
+     * with the ordering specified below.
+     */
+    public X509Name(
+        Hashtable  attributes)
+    {
+        this(null, attributes);
+    }
+
+    /**
+     * constructor from a table of attributes with ordering.
+     * <p>
+     * it's is assumed the table contains OID/String pairs, and the contents
+     * of the table are copied into an internal table as part of the 
+     * construction process. The ordering vector should contain the OIDs
+     * in the order they are meant to be encoded or printed in toString.
+     */
+    public X509Name(
+        Vector      ordering,
+        Hashtable   attributes)
+    {
+        if (ordering != null)
+        {
+            for (int i = 0; i != ordering.size(); i++)
+            {
+                this.ordering.addElement(ordering.elementAt(i));
+            }
+        }
+        else
+        {
+            Enumeration     e = attributes.keys();
+
+            while (e.hasMoreElements())
+            {
+                this.ordering.addElement(e.nextElement());
+            }
+        }
+
+        for (int i = 0; i != this.ordering.size(); i++)
+        {
+            DERObjectIdentifier     oid = (DERObjectIdentifier)this.ordering.elementAt(i);
+
+            if (OIDLookUp.get(oid) == null)
+            {
+                throw new IllegalArgumentException("Unknown object id - " + oid.getId() + " - passed to distinguished name");
+            }
+
+            if (attributes.get(oid) == null)
+            {
+                throw new IllegalArgumentException("No attribute for object id - " + oid.getId() + " - passed to distinguished name");
+            }
+
+            this.attributes.put(oid, attributes.get(oid)); // copy the hash table
+        }
+    }
+
+    /**
+     * takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
+     * some such, converting it into an ordered set of name attributes.
+     */
+    public X509Name(
+        String  dirName)
+    {
+        X509NameTokenizer   nTok = new X509NameTokenizer(dirName);
+
+        while (nTok.hasMoreTokens())
+        {
+            String  token = nTok.nextToken();
+            int     index = token.indexOf('=');
+
+            if (index == -1)
+            {
+                throw new IllegalArgumentException("badly formated directory string");
+            }
+
+            String  name = token.substring(0, index);
+            String  value = token.substring(index + 1);
+
+            DERObjectIdentifier oid = (DERObjectIdentifier)SymbolLookUp.get(name);
+            if (oid == null)
+            {
+                throw new IllegalArgumentException("Unknown object id - " + oid.getId() + " - passed to distinguished name");
+            }
+
+            this.ordering.addElement(oid);
+            this.attributes.put(oid, value);
+        }
+    }
+
+    public DERObject getDERObject()
+    {
+        if (seq == null)
+        {
+            seq = new DERConstructedSequence();
+
+            for (int i = 0; i != ordering.size(); i++)
+            {
+                DERConstructedSequence  s = new DERConstructedSequence();
+                DERObjectIdentifier     oid = (DERObjectIdentifier)ordering.elementAt(i);
+
+                s.addObject(oid);
+                if (oid.equals(EmailAddress))
+                {
+                    s.addObject(new DERIA5String((String)attributes.get(oid)));
+                }
+                else
+                {
+                    s.addObject(new DERPrintableString((String)attributes.get(oid)));
+                }
+
+                seq.addObject(new DERSet(s));
+            }
+        }
+
+        return seq;
+    }
+
+    public int hashCode()
+    {
+        Enumeration     e = attributes.keys();
+        int             hashCode = 0;
+
+        while (e.hasMoreElements())
+        {
+            Object  o = e.nextElement();
+
+            hashCode ^= o.hashCode();
+            hashCode ^= attributes.get(o).hashCode();
+        }
+
+        for (int i = 0; i != ordering.size(); i++)
+        {
+            hashCode ^= ordering.elementAt(i).hashCode();
+        }
+
+        return hashCode;
+    }
+
+    public boolean equals(
+        Object o)
+    {
+        if (o == null || !(o instanceof X509Name))
+        {
+            return false;
+        }
+
+        X509Name        other = (X509Name)o;
+
+        if (ordering.size() != other.ordering.size())
+        {
+            return false;
+        }
+
+        for (int i = 0; i != ordering.size(); i++)
+        {
+            if (!ordering.elementAt(i).equals(other.ordering.elementAt(i)))
+            {
+                return false;
+            }
+        }
+
+        Enumeration     e1 = attributes.keys();
+        Enumeration     e2 = other.attributes.keys();
+
+        while (e1.hasMoreElements() && e2.hasMoreElements())
+        {
+            Object  o1 = e1.nextElement();
+            Object  o2 = e2.nextElement();
+            
+            if (!o1.equals(o2))
+            {
+                return false;
+            }
+        }
+
+        if (e1.hasMoreElements() || e2.hasMoreElements())
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    public String toString()
+    {
+        StringBuffer    buf = new StringBuffer();
+        boolean         first = true;
+
+        for (int i = 0; i != ordering.size(); i++)
+        {
+            Object  oid = ordering.elementAt(i);
+            String  sym = (String)OIDLookUp.get(oid);
+            
+            if (first)
+            {
+                first = false;
+            }
+            else
+            {
+                buf.append(", ");
+            }
+
+            if (sym != null)
+            {
+                buf.append(sym);
+                buf.append("=");
+                buf.append((String)attributes.get(oid));
+            }
+            else
+            {
+                buf.append(((DERObjectIdentifier)oid).getId());
+                buf.append("=");
+                buf.append((String)attributes.get(oid));
+            }
+        }
+
+        return buf.toString();
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/X509NameTokenizer.java b/src/org/bouncycastle/asn1/x509/X509NameTokenizer.java
new file mode 100644 (file)
index 0000000..2474027
--- /dev/null
@@ -0,0 +1,48 @@
+package org.bouncycastle.asn1.x509;
+
+/**
+ * class for breaking up an X509 Name into it's component tokens, ala
+ * java.util.StringTokenizer. We need this class as some of the
+ * lightweight Java environment don't support classes like
+ * StringTokenizer.
+ */
+public class X509NameTokenizer
+{
+    private String  oid;
+    private int     index;
+
+    public X509NameTokenizer(
+        String oid)
+    {
+        this.oid = oid;
+        this.index = 0;
+    }
+
+    public boolean hasMoreTokens()
+    {
+        return (index != -1);
+    }
+
+    public String nextToken()
+    {
+        if (index == -1)
+        {
+            return null;
+        }
+
+        String  token;
+        int     end = oid.indexOf(',', index);
+
+        if (end == -1)
+        {
+            token = oid.substring(index);
+            index = -1;
+            return token.trim();
+        }
+
+        token = oid.substring(index, end);
+
+        index = end + 1;
+        return token.trim();
+    }
+}
diff --git a/src/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java b/src/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
new file mode 100644 (file)
index 0000000..18ce5c1
--- /dev/null
@@ -0,0 +1,38 @@
+package org.bouncycastle.asn1.x509;
+
+import org.bouncycastle.asn1.DERObjectIdentifier;
+
+public interface X509ObjectIdentifiers
+{
+    //
+    // base id
+    //
+    static final String                 id                      = "2.5.4";
+
+    static final DERObjectIdentifier    commonName              = new DERObjectIdentifier(id + ".3");
+    static final DERObjectIdentifier    countryName             = new DERObjectIdentifier(id + ".6");
+    static final DERObjectIdentifier    localityName            = new DERObjectIdentifier(id + ".7");
+    static final DERObjectIdentifier    stateOrProvinceName     = new DERObjectIdentifier(id + ".8");
+    static final DERObjectIdentifier    organization            = new DERObjectIdentifier(id + ".10");
+    static final DERObjectIdentifier    organizationalUnitName  = new DERObjectIdentifier(id + ".11");
+
+    // id-SHA1 OBJECT IDENTIFIER ::=    
+    //   {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 }    //
+    static final DERObjectIdentifier    id_SHA1                 = new DERObjectIdentifier("1.3.14.3.2.26");
+
+    //
+    // ripemd160 OBJECT IDENTIFIER ::=
+    //      {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) hashAlgorithm(2) RIPEMD-160(1)}
+    //
+    static final DERObjectIdentifier    ripemd160               = new DERObjectIdentifier("1.3.36.3.2.1");
+
+    //
+    // ripemd160WithRSAEncryption OBJECT IDENTIFIER ::=
+    //      {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) signatureAlgorithm(3) rsaSignature(1) rsaSignatureWithripemd160(2) }
+    //
+    static final DERObjectIdentifier    ripemd160WithRSAEncryption = new DERObjectIdentifier("1.3.36.3.3.1.2");
+
+
+       static final DERObjectIdentifier        id_ea_rsa = new DERObjectIdentifier("2.5.8.1.1");
+}
+
diff --git a/src/org/bouncycastle/crypto/AsymmetricBlockCipher.java b/src/org/bouncycastle/crypto/AsymmetricBlockCipher.java
new file mode 100644 (file)
index 0000000..23293f9
--- /dev/null
@@ -0,0 +1,46 @@
+package org.bouncycastle.crypto;
+
+import java.math.BigInteger;
+
+/**
+ * base interface that a public/private key block cipher needs
+ * to conform to.
+ */
+public interface AsymmetricBlockCipher
+{
+    /**
+     * initialise the cipher.
+     *
+     * @param forEncryption if true the cipher is initialised for 
+     *  encryption, if false for decryption.
+     * @param param the key and other data required by the cipher.
+     */
+    public void init(boolean forEncryption, CipherParameters param);
+
+    /**
+     * returns the largest size an input block can be.
+     *
+     * @return maximum size for an input block.
+     */
+    public int getInputBlockSize();
+
+    /**
+     * returns the maximum size of the block produced by this cipher.
+     *
+     * @return maximum size of the output block produced by the cipher.
+     */
+    public int getOutputBlockSize();
+
+    /**
+     * process the block of len bytes stored in in from offset inOff.
+     *
+     * @param in the input data
+     * @param inOff offset into the in array where the data starts
+     * @param len the length of the block to be processed.
+     * @return the resulting byte array of the encryption/decryption process.
+     * @exception InvalidCipherTextException data decrypts improperly.
+     * @exception DataLengthException the input data is too large for the cipher.
+     */
+    public byte[] processBlock(byte[] in, int inOff, int len)
+        throws InvalidCipherTextException;
+}
diff --git a/src/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java b/src/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java
new file mode 100644 (file)
index 0000000..85bec73
--- /dev/null
@@ -0,0 +1,44 @@
+package org.bouncycastle.crypto;
+
+/**
+ * a holding class for public/private parameter pairs.
+ */
+public class AsymmetricCipherKeyPair
+{
+    private CipherParameters    publicParam;
+    private CipherParameters    privateParam;
+
+    /**
+     * basic constructor.
+     *
+     * @param publicParam a public key parameters object.
+     * @param privateParam the corresponding private key parameters.
+     */
+    public AsymmetricCipherKeyPair(
+        CipherParameters    publicParam,
+        CipherParameters    privateParam)
+    {
+        this.publicParam = publicParam;
+        this.privateParam = privateParam;
+    }
+
+    /**
+     * return the public key parameters.
+     *
+     * @return the public key parameters.
+     */
+    public CipherParameters getPublic()
+    {
+        return publicParam;
+    }
+
+    /**
+     * return the private key parameters.
+     *
+     * @return the private key parameters.
+     */
+    public CipherParameters getPrivate()
+    {
+        return privateParam;
+    }
+}
diff --git a/src/org/bouncycastle/crypto/AsymmetricCipherKeyPairGenerator.java b/src/org/bouncycastle/crypto/AsymmetricCipherKeyPairGenerator.java
new file mode 100644 (file)
index 0000000..236ebbe
--- /dev/null
@@ -0,0 +1,22 @@
+package org.bouncycastle.crypto;
+
+/**
+ * interface that a public/private key pair generator should conform to.
+ */
+public interface AsymmetricCipherKeyPairGenerator
+{
+    /**
+     * intialise the key pair generator.
+     *
+     * @param the parameters the key pair is to be initialised with.
+     */
+    public void init(KeyGenerationParameters param);
+
+    /**
+     * return an AsymmetricCipherKeyPair containing the generated keys.
+     *
+     * @return an AsymmetricCipherKeyPair containing the generated keys.
+     */
+    public AsymmetricCipherKeyPair generateKeyPair();
+}
+
diff --git a/src/org/bouncycastle/crypto/BlockCipher.java b/src/org/bouncycastle/crypto/BlockCipher.java
new file mode 100644 (file)
index 0000000..0b99ee7
--- /dev/null
@@ -0,0 +1,57 @@
+package org.bouncycastle.crypto;
+
+import java.lang.IllegalStateException;
+
+/**
+ * Block cipher engines are expected to conform to this interface.
+ */
+public interface BlockCipher
+{
+    /**
+     * Initialise the cipher.
+     *
+     * @param forEncryption if true the cipher is initialised for
+     *  encryption, if false for decryption.
+     * @param param the key and other data required by the cipher.
+     * @exception IllegalArgumentException if the params argument is
+     * inappropriate.
+     */
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException;
+
+    /**
+     * Return the name of the algorithm the cipher implements.
+     *
+     * @return the name of the algorithm the cipher implements.
+     */
+    public String getAlgorithmName();
+
+    /**
+     * Return the block size for this cipher (in bytes).
+     *
+     * @return the block size for this cipher in bytes.
+     */
+    public int getBlockSize();
+
+    /**
+     * Process one block of input from the array in and write it to
+     * the out array.
+     *
+     * @param in the array containing the input data.
+     * @param inOff offset into the in array the data starts at.
+     * @param out the array the output data will be copied into.
+     * @param outOff the offset into the out array the output will start at.
+     * @exception DataLengthException if there isn't enough data in in, or
+     * space in out.
+     * @exception IllegalStateException if the cipher isn't initialised.
+     * @return the number of bytes processed and produced.
+     */
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException;
+
+    /**
+     * Reset the cipher. After resetting the cipher is in the same state
+     * as it was after the last init (if there was one).
+     */
+    public void reset();
+}
diff --git a/src/org/bouncycastle/crypto/CipherKeyGenerator.java b/src/org/bouncycastle/crypto/CipherKeyGenerator.java
new file mode 100644 (file)
index 0000000..451f8e8
--- /dev/null
@@ -0,0 +1,38 @@
+package org.bouncycastle.crypto;
+
+import java.security.SecureRandom;
+
+/**
+ * The base class for symmetric, or secret, cipher key generators.
+ */
+public class CipherKeyGenerator
+{
+    protected SecureRandom  random;
+    protected int           strength;
+
+    /**
+     * initialise the key generator.
+     *
+     * @param param the parameters to be used for key generation
+     */
+    public void init(
+        KeyGenerationParameters param)
+    {
+        this.random = param.getRandom();
+        this.strength = (param.getStrength() + 7) / 8;
+    }
+
+    /**
+     * generate a secret key.
+     *
+     * @return a byte array containing the key value.
+     */
+    public byte[] generateKey()
+    {
+        byte[]  key = new byte[strength];
+
+        random.nextBytes(key);
+
+        return key;
+    }
+}
diff --git a/src/org/bouncycastle/crypto/CipherParameters.java b/src/org/bouncycastle/crypto/CipherParameters.java
new file mode 100644 (file)
index 0000000..5be8730
--- /dev/null
@@ -0,0 +1,8 @@
+package org.bouncycastle.crypto;
+
+/**
+ * all parameter classes implement this.
+ */
+public interface CipherParameters
+{
+}
diff --git a/src/org/bouncycastle/crypto/CryptoException.java b/src/org/bouncycastle/crypto/CryptoException.java
new file mode 100644 (file)
index 0000000..dc4a8df
--- /dev/null
@@ -0,0 +1,26 @@
+package org.bouncycastle.crypto;
+
+/**
+ * the foundation class for the hard exceptions thrown by the crypto packages.
+ */
+public class CryptoException 
+    extends Exception
+{
+    /**
+     * base constructor.
+     */
+    public CryptoException()
+    {
+    }
+
+    /**
+     * create a CryptoException with the given message.
+     *
+     * @param message the message to be carried with the exception.
+     */
+    public CryptoException(
+        String  message)
+    {
+        super(message);
+    }
+}
diff --git a/src/org/bouncycastle/crypto/DataLengthException.java b/src/org/bouncycastle/crypto/DataLengthException.java
new file mode 100644 (file)
index 0000000..fbf047c
--- /dev/null
@@ -0,0 +1,29 @@
+package org.bouncycastle.crypto;
+
+/**
+ * this exception is thrown if a buffer that is meant to have output
+ * copied into it turns out to be too short, or if we've been given 
+ * insufficient input. In general this exception will get thrown rather
+ * than an ArrayOutOfBounds exception.
+ */
+public class DataLengthException 
+    extends RuntimeCryptoException
+{
+    /**
+     * base constructor.
+     */
+    public DataLengthException()
+    {
+    }
+
+    /**
+     * create a DataLengthException with the given message.
+     *
+     * @param message the message to be carried with the exception.
+     */
+    public DataLengthException(
+        String  message)
+    {
+        super(message);
+    }
+}
diff --git a/src/org/bouncycastle/crypto/Digest.java b/src/org/bouncycastle/crypto/Digest.java
new file mode 100644 (file)
index 0000000..844c28b
--- /dev/null
@@ -0,0 +1,51 @@
+package org.bouncycastle.crypto;
+
+/**
+ * interface that a message digest conforms to.
+ */
+public interface Digest
+{
+    /**
+     * return the algorithm name
+     *
+     * @return the algorithm name
+     */
+    public String getAlgorithmName();
+
+    /**
+     * return the size, in bytes, of the digest produced by this message digest.
+     *
+     * @return the size, in bytes, of the digest produced by this message digest.
+     */
+       public int getDigestSize();
+
+    /**
+     * update the message digest with a single byte.
+     *
+     * @param in the input byte to be entered.
+     */
+       public void update(byte in);
+
+    /**
+     * update the message digest with a block of bytes.
+     *
+     * @param in the byte array containing the data.
+     * @param inOff the offset into the byte array where the data starts.
+     * @param len the length of the data.
+     */
+       public void update(byte[] in, int inOff, int len);
+
+    /**
+     * close the digest, producing the final digest value. The doFinal
+     * call leaves the digest reset.
+     *
+     * @param out the array the digest is to be copied into.
+     * @param outOff the offset into the out array the digest is to start at.
+     */
+       public int doFinal(byte[] out, int outOff);
+
+    /**
+     * reset the digest back to it's initial state.
+     */
+    public void reset();
+}
diff --git a/src/org/bouncycastle/crypto/InvalidCipherTextException.java b/src/org/bouncycastle/crypto/InvalidCipherTextException.java
new file mode 100644 (file)
index 0000000..59e4b26
--- /dev/null
@@ -0,0 +1,27 @@
+package org.bouncycastle.crypto;
+
+/**
+ * this exception is thrown whenever we find something we don't expect in a
+ * message.
+ */
+public class InvalidCipherTextException 
+    extends CryptoException
+{
+    /**
+     * base constructor.
+     */
+    public InvalidCipherTextException()
+    {
+    }
+
+    /**
+     * create a InvalidCipherTextException with the given message.
+     *
+     * @param message the message to be carried with the exception.
+     */
+    public InvalidCipherTextException(
+        String  message)
+    {
+        super(message);
+    }
+}
diff --git a/src/org/bouncycastle/crypto/KeyGenerationParameters.java b/src/org/bouncycastle/crypto/KeyGenerationParameters.java
new file mode 100644 (file)
index 0000000..9a63522
--- /dev/null
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto;
+
+import java.security.SecureRandom;
+
+/**
+ * The base class for parameters to key generators.
+ */
+public class KeyGenerationParameters
+{
+    private SecureRandom    random;
+    private int             strength;
+
+    /**
+     * initialise the generator with a source of randomness
+     * and a strength (in bits).
+     *
+     * @param random the random byte source.
+     * @param strength the size, in bits, of the keys we want to produce.
+     */
+    public KeyGenerationParameters(
+        SecureRandom    random,
+        int             strength)
+    {
+        this.random = random;
+        this.strength = strength;
+    }
+
+    /**
+     * return the random source associated with this
+     * generator.
+     *
+     * @return the generators random source.
+     */
+    public SecureRandom getRandom()
+    {
+        return random;
+    }
+
+    /**
+     * return the bit strength for keys produced by this generator,
+     *
+     * @return the strength of the keys this generator produces (in bits).
+     */
+    public int getStrength()
+    {
+        return strength;
+    }
+}
diff --git a/src/org/bouncycastle/crypto/RuntimeCryptoException.java b/src/org/bouncycastle/crypto/RuntimeCryptoException.java
new file mode 100644 (file)
index 0000000..c157202
--- /dev/null
@@ -0,0 +1,26 @@
+package org.bouncycastle.crypto;
+
+/**
+ * the foundation class for the exceptions thrown by the crypto packages.
+ */
+public class RuntimeCryptoException 
+    extends RuntimeException
+{
+    /**
+     * base constructor.
+     */
+    public RuntimeCryptoException()
+    {
+    }
+
+    /**
+     * create a RuntimeCryptoException with the given message.
+     *
+     * @param message the message to be carried with the exception.
+     */
+    public RuntimeCryptoException(
+        String  message)
+    {
+        super(message);
+    }
+}
diff --git a/src/org/bouncycastle/crypto/StreamBlockCipher.java b/src/org/bouncycastle/crypto/StreamBlockCipher.java
new file mode 100644 (file)
index 0000000..bea3b6a
--- /dev/null
@@ -0,0 +1,108 @@
+package org.bouncycastle.crypto;
+
+/**
+ * a wrapper for block ciphers with a single byte block size, so that they
+ * can be treated like stream ciphers.
+ */
+public class StreamBlockCipher
+    implements StreamCipher
+{
+    private BlockCipher  cipher;
+
+    private byte[]  oneByte = new byte[1];
+
+    /**
+     * basic constructor.
+     *
+     * @param cipher the block cipher to be wrapped.
+     * @exception IllegalArgumentException if the cipher has a block size other than
+     * one.
+     */
+    public StreamBlockCipher(
+        BlockCipher cipher)
+    {
+        if (cipher.getBlockSize() != 1)
+        {
+            throw new IllegalArgumentException("block cipher block size != 1.");
+        }
+
+        this.cipher = cipher;
+    }
+
+    /**
+     * initialise the underlying cipher.
+     *
+     * @param forEncryption true if we are setting up for encryption, false otherwise.
+     * @param param the necessary parameters for the underlying cipher to be initialised.
+     */
+    public void init(
+        boolean forEncryption,
+        CipherParameters params)
+    {
+        cipher.init(forEncryption, params);
+    }
+
+    /**
+     * return the name of the algorithm we are wrapping.
+     *
+     * @return the name of the algorithm we are wrapping.
+     */
+    public String getAlgorithmName()
+    {
+        return cipher.getAlgorithmName();
+    }
+
+    /**
+     * encrypt/decrypt a single byte returning the result.
+     *
+     * @param in the byte to be processed.
+     * @return the result of processing the input byte.
+     */
+    public byte returnByte(
+        byte    in)
+    {
+        oneByte[0] = in;
+
+        cipher.processBlock(oneByte, 0, oneByte, 0);
+
+        return oneByte[0];
+    }
+
+    /**
+     * process a block of bytes from in putting the result into out.
+     * 
+     * @param in the input byte array.
+     * @param inOff the offset into the in array where the data to be processed starts.
+     * @param len the number of bytes to be processed.
+     * @param out the output buffer the processed bytes go into.   
+     * @param outOff the offset into the output byte array the processed data stars at.
+     * @exception DataLengthException if the output buffer is too small.
+     */
+    public void processBytes(
+        byte[]  in,
+        int     inOff,
+        int     len,
+        byte[]  out,
+        int     outOff)
+        throws DataLengthException
+    {
+        if (outOff + len > out.length)
+        {
+            throw new DataLengthException("output buffer too small in processBytes()");
+        }
+
+        for (int i = 0; i != len; i++)
+        {
+                cipher.processBlock(in, inOff + i, out, outOff + i);
+        }
+    }
+
+    /**
+     * reset the underlying cipher. This leaves it in the same state
+     * it was at after the last init (if there was one).
+     */
+    public void reset()
+    {
+        cipher.reset();
+    }
+}
diff --git a/src/org/bouncycastle/crypto/StreamCipher.java b/src/org/bouncycastle/crypto/StreamCipher.java
new file mode 100644 (file)
index 0000000..6886e8c
--- /dev/null
@@ -0,0 +1,53 @@
+package org.bouncycastle.crypto;
+
+/**
+ * the interface stream ciphers conform to.
+ */
+public interface StreamCipher
+{
+    /**
+     * Initialise the cipher.
+     *
+     * @param forEncryption if true the cipher is initialised for
+     *  encryption, if false for decryption.
+     * @param param the key and other data required by the cipher.
+     * @exception IllegalArgumentException if the params argument is
+     * inappropriate.
+     */
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException;
+
+    /**
+     * Return the name of the algorithm the cipher implements.
+     *
+     * @return the name of the algorithm the cipher implements.
+     */
+    public String getAlgorithmName();
+
+    /**
+     * encrypt/decrypt a single byte returning the result.
+     *
+     * @param in the byte to be processed.
+     * @return the result of processing the input byte.
+     */
+    public byte returnByte(byte in);
+
+    /**
+     * process a block of bytes from in putting the result into out.
+     *
+     * @param in the input byte array.
+     * @param inOff the offset into the in array where the data to be processed starts.
+     * @param len the number of bytes to be processed.
+     * @param out the output buffer the processed bytes go into.
+     * @param outOff the offset into the output byte array the processed data stars at.
+     * @exception DataLengthException if the output buffer is too small.
+     */
+    public void processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+        throws DataLengthException;
+
+    /**
+     * reset the cipher. This leaves it in the same state
+     * it was at after the last init (if there was one).
+     */
+    public void reset();
+}
diff --git a/src/org/bouncycastle/crypto/digests/GeneralDigest.java b/src/org/bouncycastle/crypto/digests/GeneralDigest.java
new file mode 100644 (file)
index 0000000..2d319b7
--- /dev/null
@@ -0,0 +1,128 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * base implementation of MD4 family style digest as outlined in
+ * "Handbook of Applied Cryptography", pages 344 - 347.
+ */
+public abstract class GeneralDigest
+    implements Digest
+{
+    private byte[]  xBuf;
+    private int     xBufOff;
+
+    private long    byteCount;
+
+       /**
+        * Standard constructor
+        */
+       protected GeneralDigest()
+       {
+               xBuf = new byte[4];
+               xBufOff = 0;
+       }
+
+       /**
+        * Copy constructor.  We are using copy constructors in place
+        * of the Object.clone() interface as this interface is not
+        * supported by J2ME.
+        */
+       protected GeneralDigest(GeneralDigest t)
+       {
+        xBuf = new byte[t.xBuf.length];
+               System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length);
+
+               xBufOff = t.xBufOff;
+               byteCount = t.byteCount;
+       }
+
+    public void update(
+        byte in)
+    {
+        xBuf[xBufOff++] = in;
+
+        if (xBufOff == xBuf.length)
+        {
+            processWord(xBuf, 0);
+            xBufOff = 0;
+        }
+
+        byteCount++;
+    }
+
+    public void update(
+        byte[]  in,
+        int     inOff,
+        int     len)
+    {
+        //
+        // fill the current word
+        //
+        while ((xBufOff != 0) && (len > 0))
+        {
+            update(in[inOff]);
+
+            inOff++;
+            len--;
+        }
+
+        //
+        // process whole words.
+        //
+        while (len > xBuf.length)
+        {
+            processWord(in, inOff);
+
+            inOff += xBuf.length;
+            len -= xBuf.length;
+            byteCount += xBuf.length;
+        }
+
+        //
+        // load in the remainder.
+        //
+        while (len > 0)
+        {
+            update(in[inOff]);
+
+            inOff++;
+            len--;
+        }
+    }
+
+    public void finish()
+    {
+        long    bitLength = (byteCount << 3);
+
+        //
+        // add the pad bytes.
+        //
+        update((byte)128);
+
+        while (xBufOff != 0)
+        {
+            update((byte)0);
+        }
+
+        processLength(bitLength);
+
+        processBlock();
+    }
+
+    public void reset()
+    {
+        byteCount = 0;
+
+        xBufOff = 0;
+               for ( int i = 0; i < xBuf.length; i++ ) {
+                       xBuf[i] = 0;
+               }
+    }
+
+    protected abstract void processWord(byte[] in, int inOff);
+
+    protected abstract void processLength(long bitLength);
+
+    protected abstract void processBlock();
+}
diff --git a/src/org/bouncycastle/crypto/digests/MD2Digest.java b/src/org/bouncycastle/crypto/digests/MD2Digest.java
new file mode 100644 (file)
index 0000000..88aa6a4
--- /dev/null
@@ -0,0 +1,232 @@
+package org.bouncycastle.crypto.digests;\r
+
+import org.bouncycastle.crypto.Digest;
+/**
+ * implementation of MD2
+ * as outlined in RFC1319 by B.Kaliski from RSA Laboratories April 1992
+ *
+ * @author Michael Lee
+ */
+public class MD2Digest\r
+    implements Digest\r
+{\r
+    private static final int DIGEST_LENGTH = 16;\r
+\r
+    /* X buffer */\r
+    private byte[]   X = new byte[48];\r
+    private int     xOff;
+\r    /* M buffer */
+\r    private byte[]   M = new byte[16];\r
+    private int     mOff;
+\r    /* check sum */
+\r    private byte[]   C = new byte[16];
+    private int COff;
+
+    public MD2Digest()\r
+    {\r
+        reset();\r
+    }\r
+       public MD2Digest(MD2Digest t)\r
+       {
+               System.arraycopy(t.X, 0, X, 0, t.X.length);
+               xOff = t.xOff;
+               System.arraycopy(t.M, 0, M, 0, t.M.length);
+               mOff = t.mOff;
+               System.arraycopy(t.C, 0, C, 0, t.C.length);
+               COff = t.COff;
+       }
+    /**\r
+     * return the algorithm name
+     *
+     * @return the algorithm name
+     */
+    public String getAlgorithmName()
+    {
+        return "MD2";
+    }
+    /**
+     * return the size, in bytes, of the digest produced by this message digest.
+     *
+     * @return the size, in bytes, of the digest produced by this message digest.
+     */
+       public int getDigestSize()
+    {
+        return DIGEST_LENGTH;
+    }
+    /**
+     * close the digest, producing the final digest value. The doFinal
+     * call leaves the digest reset.
+     *
+     * @param out the array the digest is to be copied into.
+     * @param outOff the offset into the out array the digest is to start at.
+     */
+       public int doFinal(byte[] out, int outOff)
+    {
+        // add padding
+        byte paddingByte = (byte)(M.length-mOff);
+        for (int i=mOff;i<M.length;i++)
+        {
+            M[i] = paddingByte;
+        }
+        //do final check sum
+        processCheckSum(M);
+        // do final block process
+        processBlock(M);
+
+        processBlock(C);
+
+        System.arraycopy(X,xOff,out,outOff,16);
+
+        reset();
+
+        return DIGEST_LENGTH;
+    }
+    /**
+     * reset the digest back to it's initial state.
+     */
+    public void reset()\r
+    {\r
+        xOff = 0;\r
+        for (int i = 0; i != X.length; i++)
+        {
+            X[i] = 0;
+        }
+        mOff = 0;\r
+        for (int i = 0; i != M.length; i++)
+        {
+            M[i] = 0;
+        }
+        COff = 0;\r
+        for (int i = 0; i != C.length; i++)
+        {
+            C[i] = 0;
+        }
+    }\r
+    /**\r
+     * update the message digest with a single byte.
+     *
+     * @param in the input byte to be entered.
+     */
+       public void update(byte in)
+    {
+        M[mOff++] = in;
+
+        if (mOff == 16)
+        {
+            processCheckSum(M);
+            processBlock(M);
+            mOff = 0;
+        }
+    }
+
+    /**
+     * update the message digest with a block of bytes.
+     *
+     * @param in the byte array containing the data.
+     * @param inOff the offset into the byte array where the data starts.
+     * @param len the length of the data.
+     */
+       public void update(byte[] in, int inOff, int len)\r
+    {\r
+        //\r
+        // fill the current word
+        //
+        while ((mOff != 0) && (len > 0))
+        {
+            update(in[inOff]);
+            inOff++;
+            len--;
+        }
+
+        //
+        // process whole words.
+        //
+        while (len > 16)
+        {
+            System.arraycopy(in,inOff,M,0,16);
+            processCheckSum(M);
+            processBlock(M);
+            len -= 16;
+            inOff += 16;
+        }
+
+        //
+        // load in the remainder.
+        //
+        while (len > 0)
+        {
+            update(in[inOff]);
+            inOff++;
+            len--;
+        }\r
+    }\r
+    protected void processCheckSum(byte[] m)\r
+    {\r
+        int L = C[15];\r
+        for (int i=0;i<16;i++)\r
+        {\r
+            C[i] ^= S[(m[i] ^ L) & 0xff];\r
+            L = C[i];\r
+        }\r
+    }\r
+    protected void processBlock(byte[] m)\r
+    {
+        for (int i=0;i<16;i++)
+        {
+            X[i+16] = m[i];
+            X[i+32] = (byte)(m[i] ^ X[i]);
+        }
+        // encrypt block
+        int t = 0;
+
+        for (int j=0;j<18;j++)
+        {
+            for (int k=0;k<48;k++)
+            {
+                t = X[k] ^= S[t];
+                t = t & 0xff;
+            }
+            t = (t + j)%256;
+        }
+     }
+     // 256-byte random permutation constructed from the digits of PI
+    private static final byte[] S = {\r
+      (byte)41,(byte)46,(byte)67,(byte)201,(byte)162,(byte)216,(byte)124,\r
+      (byte)1,(byte)61,(byte)54,(byte)84,(byte)161,(byte)236,(byte)240,\r
+      (byte)6,(byte)19,(byte)98,(byte)167,(byte)5,(byte)243,(byte)192,\r
+      (byte)199,(byte)115,(byte)140,(byte)152,(byte)147,(byte)43,(byte)217,\r
+      (byte)188,(byte)76,(byte)130,(byte)202,(byte)30,(byte)155,(byte)87,\r
+      (byte)60,(byte)253,(byte)212,(byte)224,(byte)22,(byte)103,(byte)66,\r
+      (byte)111,(byte)24,(byte)138,(byte)23,(byte)229,(byte)18,(byte)190,\r
+      (byte)78,(byte)196,(byte)214,(byte)218,(byte)158,(byte)222,(byte)73,\r
+      (byte)160,(byte)251,(byte)245,(byte)142,(byte)187,(byte)47,(byte)238,\r
+      (byte)122,(byte)169,(byte)104,(byte)121,(byte)145,(byte)21,(byte)178,\r
+      (byte)7,(byte)63,(byte)148,(byte)194,(byte)16,(byte)137,(byte)11,\r
+      (byte)34,(byte)95,(byte)33,(byte)128,(byte)127,(byte)93,(byte)154,\r
+      (byte)90,(byte)144,(byte)50,(byte)39,(byte)53,(byte)62,(byte)204,\r
+      (byte)231,(byte)191,(byte)247,(byte)151,(byte)3,(byte)255,(byte)25,\r
+      (byte)48,(byte)179,(byte)72,(byte)165,(byte)181,(byte)209,(byte)215,\r
+      (byte)94,(byte)146,(byte)42,(byte)172,(byte)86,(byte)170,(byte)198,\r
+      (byte)79,(byte)184,(byte)56,(byte)210,(byte)150,(byte)164,(byte)125,\r
+      (byte)182,(byte)118,(byte)252,(byte)107,(byte)226,(byte)156,(byte)116,\r
+      (byte)4,(byte)241,(byte)69,(byte)157,(byte)112,(byte)89,(byte)100,\r
+      (byte)113,(byte)135,(byte)32,(byte)134,(byte)91,(byte)207,(byte)101,\r
+      (byte)230,(byte)45,(byte)168,(byte)2,(byte)27,(byte)96,(byte)37,\r
+      (byte)173,(byte)174,(byte)176,(byte)185,(byte)246,(byte)28,(byte)70,\r
+      (byte)97,(byte)105,(byte)52,(byte)64,(byte)126,(byte)15,(byte)85,\r
+      (byte)71,(byte)163,(byte)35,(byte)221,(byte)81,(byte)175,(byte)58,\r
+      (byte)195,(byte)92,(byte)249,(byte)206,(byte)186,(byte)197,(byte)234,\r
+      (byte)38,(byte)44,(byte)83,(byte)13,(byte)110,(byte)133,(byte)40,\r
+      (byte)132, 9,(byte)211,(byte)223,(byte)205,(byte)244,(byte)65,\r
+      (byte)129,(byte)77,(byte)82,(byte)106,(byte)220,(byte)55,(byte)200,\r
+      (byte)108,(byte)193,(byte)171,(byte)250,(byte)36,(byte)225,(byte)123,\r
+      (byte)8,(byte)12,(byte)189,(byte)177,(byte)74,(byte)120,(byte)136,\r
+      (byte)149,(byte)139,(byte)227,(byte)99,(byte)232,(byte)109,(byte)233,\r
+      (byte)203,(byte)213,(byte)254,(byte)59,(byte)0,(byte)29,(byte)57,\r
+      (byte)242,(byte)239,(byte)183,(byte)14,(byte)102,(byte)88,(byte)208,\r
+      (byte)228,(byte)166,(byte)119,(byte)114,(byte)248,(byte)235,(byte)117,\r
+      (byte)75,(byte)10,(byte)49,(byte)68,(byte)80,(byte)180,(byte)143,\r
+      (byte)237,(byte)31,(byte)26,(byte)219,(byte)153,(byte)141,(byte)51,\r
+      (byte)159,(byte)17,(byte)131,(byte)20\r
+    };\r
+}\r
diff --git a/src/org/bouncycastle/crypto/digests/MD5Digest.java b/src/org/bouncycastle/crypto/digests/MD5Digest.java
new file mode 100644 (file)
index 0000000..7325fba
--- /dev/null
@@ -0,0 +1,303 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * implementation of MD5 as outlined in "Handbook of Applied Cryptography", pages 346 - 347.
+ */
+public class MD5Digest
+    extends GeneralDigest
+{
+    private static final int    DIGEST_LENGTH = 16;
+
+    private int     H1, H2, H3, H4;         // IV's
+
+    private int[]   X = new int[16];
+    private int     xOff;
+
+       /**
+        * Standard constructor
+        */
+    public MD5Digest()
+    {
+        reset();
+    }
+
+       /**
+        * Copy constructor.  This will copy the state of the provided
+        * message digest.
+        */
+       public MD5Digest(MD5Digest t)
+       {
+               super(t);
+
+               H1 = t.H1;
+               H2 = t.H2;
+               H3 = t.H3;
+               H4 = t.H4;
+
+               System.arraycopy(t.X, 0, X, 0, t.X.length);
+               xOff = t.xOff;
+       }
+
+    public String getAlgorithmName()
+    {
+        return "MD5";
+    }
+
+    public int getDigestSize()
+    {
+        return DIGEST_LENGTH;
+    }
+
+    protected void processWord(
+        byte[]  in,
+        int     inOff)
+    {
+        X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8)
+            | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24); 
+
+        if (xOff == 16)
+        {
+            processBlock();
+        }
+    }
+
+    protected void processLength(
+        long    bitLength)
+    {
+        if (xOff > 14)
+        {
+            processBlock();
+        }
+
+        X[14] = (int)(bitLength & 0xffffffff);
+        X[15] = (int)(bitLength >>> 32);
+    }
+
+    private void unpackWord(
+        int     word,
+        byte[]  out,
+        int     outOff)
+    {
+        out[outOff]     = (byte)word;
+        out[outOff + 1] = (byte)(word >>> 8);
+        out[outOff + 2] = (byte)(word >>> 16);
+        out[outOff + 3] = (byte)(word >>> 24);
+    }
+
+    public int doFinal(
+        byte[]  out,
+        int     outOff)
+    {
+        finish();
+
+        unpackWord(H1, out, outOff);
+        unpackWord(H2, out, outOff + 4);
+        unpackWord(H3, out, outOff + 8);
+        unpackWord(H4, out, outOff + 12);
+
+        reset();
+
+        return DIGEST_LENGTH;
+    }
+
+    /**
+     * reset the chaining variables to the IV values.
+     */
+    public void reset()
+    {
+        super.reset();
+
+        H1 = 0x67452301;
+        H2 = 0xefcdab89;
+        H3 = 0x98badcfe;
+        H4 = 0x10325476;
+
+        xOff = 0;
+
+        for (int i = 0; i != X.length; i++)
+        {
+            X[i] = 0;
+        }
+    }
+
+    //
+    // round 1 left rotates
+    //
+    private static final int S11 = 7;
+    private static final int S12 = 12;
+    private static final int S13 = 17;
+    private static final int S14 = 22;
+
+    //
+    // round 2 left rotates
+    //
+    private static final int S21 = 5;
+    private static final int S22 = 9;
+    private static final int S23 = 14;
+    private static final int S24 = 20;
+
+    //
+    // round 3 left rotates
+    //
+    private static final int S31 = 4;
+    private static final int S32 = 11;
+    private static final int S33 = 16;
+    private static final int S34 = 23;
+
+    //
+    // round 4 left rotates
+    //
+    private static final int S41 = 6;
+    private static final int S42 = 10;
+    private static final int S43 = 15;
+    private static final int S44 = 21;
+
+    /*
+     * rotate int x left n bits.
+     */
+    private int rotateLeft(
+        int x,
+        int n)
+    {
+        return (x << n) | (x >>> (32 - n));
+    }
+
+    /*
+     * F, G, H and I are the basic MD5 functions.
+     */
+    private int F(
+        int u,
+        int v,
+        int w)
+    {
+        return (u & v) | (~u & w);
+    }
+
+    private int G(
+        int u,
+        int v,
+        int w)
+    {
+        return (u & w) | (v & ~w);
+    }
+
+    private int H(
+        int u,
+        int v,
+        int w)
+    {
+        return u ^ v ^ w;
+    }
+
+    private int K(
+        int u,
+        int v,
+        int w)
+    {
+        return v ^ (u | ~w);
+    }
+
+    protected void processBlock()
+    {
+        int a = H1;
+        int b = H2;
+        int c = H3;
+        int d = H4;
+
+        //
+        // Round 1 - F cycle, 16 times.
+        //
+        a = rotateLeft((a + F(b, c, d) + X[ 0] + 0xd76aa478), S11) + b;
+        d = rotateLeft((d + F(a, b, c) + X[ 1] + 0xe8c7b756), S12) + a;
+        c = rotateLeft((c + F(d, a, b) + X[ 2] + 0x242070db), S13) + d;
+        b = rotateLeft((b + F(c, d, a) + X[ 3] + 0xc1bdceee), S14) + c;
+        a = rotateLeft((a + F(b, c, d) + X[ 4] + 0xf57c0faf), S11) + b;
+        d = rotateLeft((d + F(a, b, c) + X[ 5] + 0x4787c62a), S12) + a;
+        c = rotateLeft((c + F(d, a, b) + X[ 6] + 0xa8304613), S13) + d;
+        b = rotateLeft((b + F(c, d, a) + X[ 7] + 0xfd469501), S14) + c;
+        a = rotateLeft((a + F(b, c, d) + X[ 8] + 0x698098d8), S11) + b;
+        d = rotateLeft((d + F(a, b, c) + X[ 9] + 0x8b44f7af), S12) + a;
+        c = rotateLeft((c + F(d, a, b) + X[10] + 0xffff5bb1), S13) + d;
+        b = rotateLeft((b + F(c, d, a) + X[11] + 0x895cd7be), S14) + c;
+        a = rotateLeft((a + F(b, c, d) + X[12] + 0x6b901122), S11) + b;
+        d = rotateLeft((d + F(a, b, c) + X[13] + 0xfd987193), S12) + a;
+        c = rotateLeft((c + F(d, a, b) + X[14] + 0xa679438e), S13) + d;
+        b = rotateLeft((b + F(c, d, a) + X[15] + 0x49b40821), S14) + c;
+
+        //
+        // Round 2 - G cycle, 16 times.
+        //
+        a = rotateLeft((a + G(b, c, d) + X[ 1] + 0xf61e2562), S21) + b;
+        d = rotateLeft((d + G(a, b, c) + X[ 6] + 0xc040b340), S22) + a;
+        c = rotateLeft((c + G(d, a, b) + X[11] + 0x265e5a51), S23) + d;
+        b = rotateLeft((b + G(c, d, a) + X[ 0] + 0xe9b6c7aa), S24) + c;
+        a = rotateLeft((a + G(b, c, d) + X[ 5] + 0xd62f105d), S21) + b;
+        d = rotateLeft((d + G(a, b, c) + X[10] + 0x02441453), S22) + a;
+        c = rotateLeft((c + G(d, a, b) + X[15] + 0xd8a1e681), S23) + d;
+        b = rotateLeft((b + G(c, d, a) + X[ 4] + 0xe7d3fbc8), S24) + c;
+        a = rotateLeft((a + G(b, c, d) + X[ 9] + 0x21e1cde6), S21) + b;
+        d = rotateLeft((d + G(a, b, c) + X[14] + 0xc33707d6), S22) + a;
+        c = rotateLeft((c + G(d, a, b) + X[ 3] + 0xf4d50d87), S23) + d;
+        b = rotateLeft((b + G(c, d, a) + X[ 8] + 0x455a14ed), S24) + c;
+        a = rotateLeft((a + G(b, c, d) + X[13] + 0xa9e3e905), S21) + b;
+        d = rotateLeft((d + G(a, b, c) + X[ 2] + 0xfcefa3f8), S22) + a;
+        c = rotateLeft((c + G(d, a, b) + X[ 7] + 0x676f02d9), S23) + d;
+        b = rotateLeft((b + G(c, d, a) + X[12] + 0x8d2a4c8a), S24) + c;
+
+        //
+        // Round 3 - H cycle, 16 times.
+        //
+        a = rotateLeft((a + H(b, c, d) + X[ 5] + 0xfffa3942), S31) + b;
+        d = rotateLeft((d + H(a, b, c) + X[ 8] + 0x8771f681), S32) + a;
+        c = rotateLeft((c + H(d, a, b) + X[11] + 0x6d9d6122), S33) + d;
+        b = rotateLeft((b + H(c, d, a) + X[14] + 0xfde5380c), S34) + c;
+        a = rotateLeft((a + H(b, c, d) + X[ 1] + 0xa4beea44), S31) + b;
+        d = rotateLeft((d + H(a, b, c) + X[ 4] + 0x4bdecfa9), S32) + a;
+        c = rotateLeft((c + H(d, a, b) + X[ 7] + 0xf6bb4b60), S33) + d;
+        b = rotateLeft((b + H(c, d, a) + X[10] + 0xbebfbc70), S34) + c;
+        a = rotateLeft((a + H(b, c, d) + X[13] + 0x289b7ec6), S31) + b;
+        d = rotateLeft((d + H(a, b, c) + X[ 0] + 0xeaa127fa), S32) + a;
+        c = rotateLeft((c + H(d, a, b) + X[ 3] + 0xd4ef3085), S33) + d;
+        b = rotateLeft((b + H(c, d, a) + X[ 6] + 0x04881d05), S34) + c;
+        a = rotateLeft((a + H(b, c, d) + X[ 9] + 0xd9d4d039), S31) + b;
+        d = rotateLeft((d + H(a, b, c) + X[12] + 0xe6db99e5), S32) + a;
+        c = rotateLeft((c + H(d, a, b) + X[15] + 0x1fa27cf8), S33) + d;
+        b = rotateLeft((b + H(c, d, a) + X[ 2] + 0xc4ac5665), S34) + c;
+
+        //
+        // Round 4 - K cycle, 16 times.
+        //
+        a = rotateLeft((a + K(b, c, d) + X[ 0] + 0xf4292244), S41) + b;
+        d = rotateLeft((d + K(a, b, c) + X[ 7] + 0x432aff97), S42) + a;
+        c = rotateLeft((c + K(d, a, b) + X[14] + 0xab9423a7), S43) + d;
+        b = rotateLeft((b + K(c, d, a) + X[ 5] + 0xfc93a039), S44) + c;
+        a = rotateLeft((a + K(b, c, d) + X[12] + 0x655b59c3), S41) + b;
+        d = rotateLeft((d + K(a, b, c) + X[ 3] + 0x8f0ccc92), S42) + a;
+        c = rotateLeft((c + K(d, a, b) + X[10] + 0xffeff47d), S43) + d;
+        b = rotateLeft((b + K(c, d, a) + X[ 1] + 0x85845dd1), S44) + c;
+        a = rotateLeft((a + K(b, c, d) + X[ 8] + 0x6fa87e4f), S41) + b;
+        d = rotateLeft((d + K(a, b, c) + X[15] + 0xfe2ce6e0), S42) + a;
+        c = rotateLeft((c + K(d, a, b) + X[ 6] + 0xa3014314), S43) + d;
+        b = rotateLeft((b + K(c, d, a) + X[13] + 0x4e0811a1), S44) + c;
+        a = rotateLeft((a + K(b, c, d) + X[ 4] + 0xf7537e82), S41) + b;
+        d = rotateLeft((d + K(a, b, c) + X[11] + 0xbd3af235), S42) + a;
+        c = rotateLeft((c + K(d, a, b) + X[ 2] + 0x2ad7d2bb), S43) + d;
+        b = rotateLeft((b + K(c, d, a) + X[ 9] + 0xeb86d391), S44) + c;
+
+        H1 += a;
+        H2 += b;
+        H3 += c;
+        H4 += d;
+
+        //
+        // reset the offset and clean out the word buffer.
+        //
+        xOff = 0;
+        for (int i = 0; i != X.length; i++)
+        {
+            X[i] = 0;
+        }
+    }
+}
diff --git a/src/org/bouncycastle/crypto/digests/SHA1Digest.java b/src/org/bouncycastle/crypto/digests/SHA1Digest.java
new file mode 100644 (file)
index 0000000..7a7d176
--- /dev/null
@@ -0,0 +1,259 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.Digest;
+
+/**
+ * implementation of SHA-1 as outlined in "Handbook of Applied Cryptography", pages 346 - 349.
+ *
+ * It is interesting to ponder why the, apart from the extra IV, the other difference here from MD5
+ * is the "endienness" of the word processing!
+ */
+public class SHA1Digest
+    extends GeneralDigest
+{
+    private static final int    DIGEST_LENGTH = 20;
+
+    private int     H1, H2, H3, H4, H5;
+
+    private int[]   X = new int[80];
+    private int     xOff;
+
+       /**
+        * Standard constructor
+        */
+    public SHA1Digest()
+    {
+        reset();
+    }
+
+       /**
+        * Copy constructor.  This will copy the state of the provided
+        * message digest.
+        */
+       public SHA1Digest(SHA1Digest t)
+       {
+               super(t);
+
+               H1 = t.H1;
+               H2 = t.H2;
+               H3 = t.H3;
+               H4 = t.H4;
+               H5 = t.H5;
+
+               System.arraycopy(t.X, 0, X, 0, t.X.length);
+               xOff = t.xOff;
+       }
+
+    public String getAlgorithmName()
+    {
+        return "SHA-1";
+    }
+
+    public int getDigestSize()
+    {
+        return DIGEST_LENGTH;
+    }
+
+    protected void processWord(
+        byte[]  in,
+        int     inOff)
+    {
+        X[xOff++] = ((in[inOff] & 0xff) << 24) | ((in[inOff + 1] & 0xff) << 16)
+                    | ((in[inOff + 2] & 0xff) << 8) | ((in[inOff + 3] & 0xff)); 
+
+        if (xOff == 16)
+        {
+            processBlock();
+        }
+    }
+
+    private void unpackWord(
+        int     word,
+        byte[]  out,
+        int     outOff)
+    {
+        out[outOff]     = (byte)(word >>> 24);
+        out[outOff + 1] = (byte)(word >>> 16);
+        out[outOff + 2] = (byte)(word >>> 8);
+        out[outOff + 3] = (byte)word;
+    }
+
+    protected void processLength(
+        long    bitLength)
+    {
+        if (xOff > 14)
+        {
+            processBlock();
+        }
+
+        X[14] = (int)(bitLength >>> 32);
+        X[15] = (int)(bitLength & 0xffffffff);
+    }
+
+    public int doFinal(
+        byte[]  out,
+        int     outOff)
+    {
+        finish();
+
+        unpackWord(H1, out, outOff);
+        unpackWord(H2, out, outOff + 4);
+        unpackWord(H3, out, outOff + 8);
+        unpackWord(H4, out, outOff + 12);
+        unpackWord(H5, out, outOff + 16);
+
+        reset();
+
+        return DIGEST_LENGTH;
+    }
+
+    /**
+     * reset the chaining variables
+     */
+    public void reset()
+    {
+        super.reset();
+
+        H1 = 0x67452301;
+        H2 = 0xefcdab89;
+        H3 = 0x98badcfe;
+        H4 = 0x10325476;
+        H5 = 0xc3d2e1f0;
+
+        xOff = 0;
+        for (int i = 0; i != X.length; i++)
+        {
+            X[i] = 0;
+        }
+    }
+
+    //
+    // Additive constants
+    //
+    private static final int    Y1 = 0x5a827999;
+    private static final int    Y2 = 0x6ed9eba1;
+    private static final int    Y3 = 0x8f1bbcdc;
+    private static final int    Y4 = 0xca62c1d6;
+
+    private int f(
+        int    u,
+        int    v,
+        int    w)
+    {
+        return ((u & v) | ((~u) & w));
+    }
+
+    private int h(
+        int    u,
+        int    v,
+        int    w)
+    {
+        return (u ^ v ^ w);
+    }
+
+    private int g(
+        int    u,
+        int    v,
+        int    w)
+    {
+        return ((u & v) | (u & w) | (v & w));
+    }
+
+    private int rotateLeft(
+        int    x,
+        int    n)
+    {
+        return (x << n) | (x >>> (32 - n));
+    }
+
+    protected void processBlock()
+    {
+        //
+        // expand 16 word block into 80 word block.
+        //
+        for (int i = 16; i <= 79; i++)
+        {
+            X[i] = rotateLeft((X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16]), 1);
+        }
+
+        //
+        // set up working variables.
+        //
+        int     A = H1;
+        int     B = H2;
+        int     C = H3;
+        int     D = H4;
+        int     E = H5;
+
+        //
+        // round 1
+        //
+        for (int j = 0; j <= 19; j++)
+        {
+            int     t = rotateLeft(A, 5) + f(B, C, D) + E + X[j] + Y1;
+
+            E = D;
+            D = C;
+            C = rotateLeft(B, 30);
+            B = A;
+            A = t;
+        }
+
+        //
+        // round 2
+        //
+        for (int j = 20; j <= 39; j++)
+        {
+            int     t = rotateLeft(A, 5) + h(B, C, D) + E + X[j] + Y2;
+
+            E = D;
+            D = C;
+            C = rotateLeft(B, 30);
+            B = A;
+            A = t;
+        }
+
+        //
+        // round 3
+        //
+        for (int j = 40; j <= 59; j++)
+        {
+            int     t = rotateLeft(A, 5) + g(B, C, D) + E + X[j] + Y3;
+
+            E = D;
+            D = C;
+            C = rotateLeft(B, 30);
+            B = A;
+            A = t;
+        }
+
+        //
+        // round 4
+        //
+        for (int j = 60; j <= 79; j++)
+        {
+            int     t = rotateLeft(A, 5) + h(B, C, D) + E + X[j] + Y4;
+
+            E = D;
+            D = C;
+            C = rotateLeft(B, 30);
+            B = A;
+            A = t;
+        }
+
+        H1 += A;
+        H2 += B;
+        H3 += C;
+        H4 += D;
+        H5 += E;
+
+        //
+        // reset the offset and clean out the word buffer.
+        //
+        xOff = 0;
+        for (int i = 0; i != X.length; i++)
+        {
+            X[i] = 0;
+        }
+    }
+}
diff --git a/src/org/bouncycastle/crypto/encodings/PKCS1Encoding.java b/src/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
new file mode 100644 (file)
index 0000000..aa4ee61
--- /dev/null
@@ -0,0 +1,199 @@
+package org.bouncycastle.crypto.encodings;
+
+import java.math.BigInteger;
+import java.util.Random;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+/**
+ * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this
+ * depends on your application - see PKCS1 Version 2 for details.
+ */
+public class PKCS1Encoding
+    implements AsymmetricBlockCipher
+{
+    private static int      HEADER_LENGTH = 10;
+
+    // HACK
+    //private SecureRandom            random;
+    private Random            random = new Random();
+
+    private AsymmetricBlockCipher   engine;
+    private boolean                 forEncryption;
+    private boolean                 forPrivateKey;
+
+    public PKCS1Encoding(
+        AsymmetricBlockCipher   cipher)
+    {
+        this.engine = cipher;
+    }   
+
+    public AsymmetricBlockCipher getUnderlyingCipher()
+    {
+        return engine;
+    }
+
+    public void init(
+        boolean             forEncryption,
+        CipherParameters    param)
+    {
+        AsymmetricKeyParameter  kParam;
+        if (param instanceof ParametersWithRandom)
+        {
+            ParametersWithRandom    rParam = (ParametersWithRandom)param;
+
+            // HACK
+            //this.random = rParam.getRandom();
+            kParam = (AsymmetricKeyParameter)rParam.getParameters();
+        }
+        else
+        {
+            // HACK
+            //this.random = new SecureRandom();
+            kParam = (AsymmetricKeyParameter)param;
+        }
+
+        engine.init(forEncryption, kParam);
+
+        this.forPrivateKey = kParam.isPrivate();
+        this.forEncryption = forEncryption;
+    }
+
+    public int getInputBlockSize()
+    {
+        int     baseBlockSize = engine.getInputBlockSize();
+
+        if (forEncryption)
+        {
+            return baseBlockSize - HEADER_LENGTH;
+        }
+        else
+        {
+            return baseBlockSize;
+        }
+    }
+
+    public int getOutputBlockSize()
+    {
+        int     baseBlockSize = engine.getOutputBlockSize();
+
+        if (forEncryption)
+        {
+            return baseBlockSize;
+        }
+        else
+        {
+            return baseBlockSize - HEADER_LENGTH;
+        }
+    }
+
+    public byte[] processBlock(
+        byte[]  in,
+        int     inOff,
+        int     inLen)
+        throws InvalidCipherTextException
+    {
+        if (forEncryption)
+        {
+            return encodeBlock(in, inOff, inLen);
+        }
+        else
+        {
+            return decodeBlock(in, inOff, inLen);
+        }
+    }
+
+    private byte[] encodeBlock(
+        byte[]  in,
+        int     inOff,
+        int     inLen)
+        throws InvalidCipherTextException
+    {
+        byte[]  block = new byte[engine.getInputBlockSize()];
+
+        if (forPrivateKey)
+        {
+            block[0] = 0x01;                        // type code 1
+
+            for (int i = 1; i != block.length - inLen - 1; i++)
+            {
+                block[i] = (byte)0xFF;
+            }
+        }
+        else
+        {
+            random.nextBytes(block);                // random fill
+
+            block[0] = 0x02;                        // type code 2
+
+            //
+            // a zero byte marks the end of the padding, so all
+            // the pad bytes must be non-zero.
+            //
+            for (int i = 1; i != block.length - inLen - 1; i++)
+            {
+                while (block[i] == 0)
+                {
+                    block[i] = (byte)random.nextInt();
+                }
+            }
+        }
+
+        block[block.length - inLen - 1] = 0x00;       // mark the end of the padding
+        System.arraycopy(in, inOff, block, block.length - inLen, inLen);
+
+        return engine.processBlock(block, 0, block.length);
+    }
+
+    /**
+     * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format.
+     */
+    private byte[] decodeBlock(
+        byte[]  in,
+        int     inOff,
+        int     inLen)
+        throws InvalidCipherTextException
+    {
+        byte[]  block = engine.processBlock(in, inOff, inLen);
+        if (block.length < getOutputBlockSize())
+        {
+            throw new InvalidCipherTextException("block truncated");
+        }
+
+        if (block[0] != 1 && block[0] != 2)
+        {
+            throw new InvalidCipherTextException("unknown block type");
+        }
+
+        //
+        // find and extract the message block.
+        //
+        int start;
+
+        for (start = 1; start != block.length; start++)
+        {
+            if (block[start] == 0)
+            {
+                break;
+            }
+        }
+
+        start++;           // data should start at the next byte
+
+        if (start >= block.length || start < HEADER_LENGTH)
+        {
+            throw new InvalidCipherTextException("no data in block");
+        }
+
+        byte[]  result = new byte[block.length - start];
+
+        System.arraycopy(block, start, result, 0, result.length);
+
+        return result;
+    }
+}
diff --git a/src/org/bouncycastle/crypto/engines/RC4Engine.java b/src/org/bouncycastle/crypto/engines/RC4Engine.java
new file mode 100644 (file)
index 0000000..0adc923
--- /dev/null
@@ -0,0 +1,144 @@
+package org.bouncycastle.crypto.engines;\r
+\r
+import org.bouncycastle.crypto.StreamCipher;\r
+import org.bouncycastle.crypto.CipherParameters;\r
+import org.bouncycastle.crypto.DataLengthException;\r
+import org.bouncycastle.crypto.params.KeyParameter;\r
+\r
+public class RC4Engine implements StreamCipher\r
+{\r
+    private final static int STATE_LENGTH = 256;\r
+\r
+    /*\r
+     * variables to hold the state of the RC4 engine\r
+     * during encryption and decryption\r
+     */\r
+\r
+    private byte[]      engineState = null;\r
+    private int         x = 0;\r
+    private int         y = 0;\r
+    private byte[]      workingKey = null;\r
+\r
+    /**\r
+     * initialise a RC4 cipher.\r
+     *\r
+     * @param forEncryption whether or not we are for encryption.\r
+     * @param params the parameters required to set up the cipher.\r
+     * @exception IllegalArgumentException if the params argument is\r
+     * inappropriate.\r
+     */\r
+    public void init(\r
+        boolean             forEncryption, \r
+        CipherParameters     params\r
+    )\r
+    {\r
+        if (params instanceof KeyParameter)\r
+        {\r
+            /* \r
+             * RC4 encryption and decryption is completely\r
+             * symmetrical, so the 'forEncryption' is \r
+             * irrelevant.\r
+             */\r
+            workingKey = ((KeyParameter)params).getKey();\r
+            setKey(workingKey);\r
+\r
+            return;\r
+        }\r
+\r
+        throw new IllegalArgumentException("invalid parameter passed to RC4 init - " + params.getClass().getName());\r
+    }\r
+\r
+    public String getAlgorithmName()\r
+    {\r
+        return "RC4";\r
+    }\r
+\r
+    public byte returnByte(byte in)\r
+    {\r
+        x = (x + 1) & 0xff;\r
+        y = (engineState[x] + y) & 0xff;\r
+\r
+        // swap\r
+        byte tmp = engineState[x];\r
+        engineState[x] = engineState[y];\r
+        engineState[y] = tmp;\r
+\r
+        // xor\r
+        return (byte)(in ^ engineState[(engineState[x] + engineState[y]) & 0xff]);\r
+    }\r
+\r
+    public void processBytes(\r
+        byte[]     in, \r
+        int     inOff, \r
+        int     len, \r
+        byte[]     out, \r
+        int     outOff\r
+    )\r
+    {\r
+        if ((inOff + len) > in.length)\r
+        {\r
+            throw new DataLengthException("input buffer too short");\r
+        }\r
+\r
+        if ((outOff + len) > out.length)\r
+        {\r
+            throw new DataLengthException("output buffer too short");\r
+        }\r
+\r
+        for (int i = 0; i < len ; i++)\r
+        {\r
+            x = (x + 1) & 0xff;\r
+            y = (engineState[x] + y) & 0xff;\r
+\r
+            // swap\r
+            byte tmp = engineState[x];\r
+            engineState[x] = engineState[y];\r
+            engineState[y] = tmp;\r
+\r
+            // xor\r
+            out[i+outOff] = (byte)(in[i + inOff]\r
+                    ^ engineState[(engineState[x] + engineState[y]) & 0xff]);\r
+        }\r
+    }\r
+\r
+    public void reset()\r
+    {\r
+        setKey(workingKey);\r
+    }\r
+\r
+    // Private implementation\r
+\r
+    private void setKey(byte[] keyBytes)\r
+    {\r
+        workingKey = keyBytes;\r
+\r
+        // System.out.println("the key length is ; "+ workingKey.length);\r
+\r
+        x = 0;\r
+        y = 0;\r
+\r
+        if (engineState == null)\r
+        {\r
+            engineState = new byte[STATE_LENGTH];\r
+        }\r
+\r
+        // reset the state of the engine\r
+        for (int i=0; i < STATE_LENGTH; i++)\r
+        {\r
+            engineState[i] = (byte)i;\r
+        }\r
+        \r
+        int i1 = 0;\r
+        int i2 = 0;\r
+\r
+        for (int i=0; i < STATE_LENGTH; i++)\r
+        {\r
+            i2 = ((keyBytes[i1] & 0xff) + engineState[i] + i2) & 0xff;\r
+            // do the byte-swap inline\r
+            byte tmp = engineState[i];\r
+            engineState[i] = engineState[i2];\r
+            engineState[i2] = tmp;\r
+            i1 = (i1+1) % keyBytes.length; \r
+        }\r
+    }\r
+}\r
diff --git a/src/org/bouncycastle/crypto/engines/RSAEngine.java b/src/org/bouncycastle/crypto/engines/RSAEngine.java
new file mode 100644 (file)
index 0000000..2cbac54
--- /dev/null
@@ -0,0 +1,195 @@
+package org.bouncycastle.crypto.engines;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+
+/**
+ * this does your basic RSA algorithm.
+ */
+public class RSAEngine
+    implements AsymmetricBlockCipher
+{
+    private RSAKeyParameters        key;
+    private boolean                 forEncryption;
+
+    /**
+     * initialise the RSA engine.
+     *
+     * @param forEncryption treu if we are encrypting, false otherwise.
+     * @param param the necessary RSA key parameters.
+     */
+    public void init(
+        boolean             forEncryption,
+        CipherParameters    param)
+    {
+        this.key = (RSAKeyParameters)param;
+        this.forEncryption = forEncryption;
+    }
+
+    /**
+     * Return the maximum size for an input block to this engine.
+     * For RSA this is always one byte less than the key size on
+     * encryption, and the same length as the key size on decryption.
+     *
+     * @return maximum size for an input block.
+     */
+    public int getInputBlockSize()
+    {
+        int     bitSize = key.getModulus().bitLength();
+
+        if (forEncryption)
+        {
+            if ((bitSize % 8) == 0)
+            {
+                return bitSize / 8 - 1;
+            }
+
+            return bitSize / 8;
+        }
+        else
+        {
+            return (bitSize + 7) / 8;
+        }
+    }
+
+    /**
+     * Return the maximum size for an output block to this engine.
+     * For RSA this is always one byte less than the key size on
+     * decryption, and the same length as the key size on encryption.
+     *
+     * @return maximum size for an input block.
+     */
+    public int getOutputBlockSize()
+    {
+        int     bitSize = key.getModulus().bitLength();
+
+        if (forEncryption)
+        {
+            return ((bitSize - 1) + 7) / 8;
+        }
+        else
+        {
+            return (bitSize - 7) / 8;
+        }
+    }
+
+    /**
+     * Process a single block using the basic RSA algorithm.
+     *
+     * @param in the input array.
+     * @param inOff the offset into the input buffer where the data starts.
+     * @param inLen the length of the data to be processed.
+     * @return the result of the RSA process.
+     * @exception DataLengthException the input block is too large.
+     */
+    public byte[] processBlock(
+        byte[]  in,
+        int     inOff,
+        int     inLen)
+    {
+        if (inLen > (getInputBlockSize() + 1))
+        {
+            throw new DataLengthException("input too large for RSA cipher.\n");
+        }
+        else if (inLen == (getInputBlockSize() + 1) && (in[inOff] & 0x80) != 0)
+        {
+            throw new DataLengthException("input too large for RSA cipher.\n");
+        }
+
+        byte[]  block;
+
+        if (inOff != 0 || inLen != in.length)
+        {
+            block = new byte[inLen];
+
+            System.arraycopy(in, inOff, block, 0, inLen);
+        }
+        else
+        {
+            block = in;
+        }
+
+        BigInteger  input = new BigInteger(1, block);
+        byte[]      output;
+
+        if (key instanceof RSAPrivateCrtKeyParameters)
+        {
+            //
+            // we have the extra factors, use the Chinese Remainder Theorem - the author
+            // wishes to express his thanks to Dirk Bonekaemper at rtsffm.com for 
+            // advice regarding the expression of this.
+            //
+            RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key;
+
+            BigInteger d = crtKey.getExponent();
+            BigInteger p = crtKey.getP();
+            BigInteger q = crtKey.getQ();
+            BigInteger dP = crtKey.getDP();
+            BigInteger dQ = crtKey.getDQ();
+            BigInteger qInv = crtKey.getQInv();
+    
+            BigInteger mP, mQ, h, m;
+    
+            // mP = ((input mod p) ^ dP)) mod p
+            mP = (input.remainder(p)).modPow(dP, p);
+    
+            // mQ = ((input mod q) ^ dQ)) mod q
+            mQ = (input.remainder(q)).modPow(dQ, q);
+    
+            // h = qInv * (mP - mQ) mod p
+            h = mP.subtract(mQ);
+            h = h.multiply(qInv);
+            h = h.mod(p);               // mod (in Java) returns the positive residual
+    
+            // m = h * q + mQ
+            m = h.multiply(q);
+            m = m.add(mQ);
+    
+            output = m.toByteArray();
+        }
+        else
+        {
+            output = input.modPow(
+                        key.getExponent(), key.getModulus()).toByteArray();
+        }
+
+        if (forEncryption)
+        {
+            if (output[0] == 0 && output.length > getOutputBlockSize())        // have ended up with an extra zero byte, copy down.
+            {
+                byte[]  tmp = new byte[output.length - 1];
+
+                System.arraycopy(output, 1, tmp, 0, tmp.length);
+
+                return tmp;
+            }
+
+            if (output.length < getOutputBlockSize())     // have ended up with less bytes than normal, lengthen
+            {
+                byte[]  tmp = new byte[getOutputBlockSize()];
+
+                System.arraycopy(output, 0, tmp, tmp.length - output.length, output.length);
+
+                return tmp;
+            }
+        }
+        else
+        {
+            if (output[0] == 0)        // have ended up with an extra zero byte, copy down.
+            {
+                byte[]  tmp = new byte[output.length - 1];
+
+                System.arraycopy(output, 1, tmp, 0, tmp.length);
+
+                return tmp;
+            }
+        }
+        return output;
+    }
+}
diff --git a/src/org/bouncycastle/crypto/params/AsymmetricKeyParameter.java b/src/org/bouncycastle/crypto/params/AsymmetricKeyParameter.java
new file mode 100644 (file)
index 0000000..5710395
--- /dev/null
@@ -0,0 +1,20 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class AsymmetricKeyParameter
+       implements CipherParameters
+{
+    boolean privateKey;
+
+    public AsymmetricKeyParameter(
+        boolean privateKey)
+    {
+        this.privateKey = privateKey;
+    }
+
+    public boolean isPrivate()
+    {
+        return privateKey;
+    }
+}
diff --git a/src/org/bouncycastle/crypto/params/KeyParameter.java b/src/org/bouncycastle/crypto/params/KeyParameter.java
new file mode 100644 (file)
index 0000000..5a1c39d
--- /dev/null
@@ -0,0 +1,30 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class KeyParameter
+       implements CipherParameters
+{
+    private byte[]  key;
+
+    public KeyParameter(
+        byte[]  key)
+    {
+        this(key, 0, key.length);
+    }
+
+    public KeyParameter(
+        byte[]  key,
+        int     keyOff,
+        int     keyLen)
+    {
+        this.key = new byte[keyLen];
+
+        System.arraycopy(key, keyOff, this.key, 0, keyLen);
+    }
+
+    public byte[] getKey()
+    {
+        return key;
+    }
+}
diff --git a/src/org/bouncycastle/crypto/params/ParametersWithRandom.java b/src/org/bouncycastle/crypto/params/ParametersWithRandom.java
new file mode 100644 (file)
index 0000000..f8b7c82
--- /dev/null
@@ -0,0 +1,41 @@
+package org.bouncycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class ParametersWithRandom
+    implements CipherParameters
+{
+    private SecureRandom        random;
+    private CipherParameters    parameters;
+
+    public ParametersWithRandom(
+        CipherParameters    parameters,
+        SecureRandom        random)
+    {
+        this.random = random;
+        this.parameters = parameters;
+    }
+
+    public ParametersWithRandom(
+        CipherParameters    parameters)
+    {
+        this.random = null;
+        this.parameters = parameters;
+    }
+
+    public SecureRandom getRandom()
+    {
+        if (random == null)
+        {
+            random = new SecureRandom();
+        }
+        return random;
+    }
+
+    public CipherParameters getParameters()
+    {
+        return parameters;
+    }
+}
diff --git a/src/org/bouncycastle/crypto/params/RSAKeyParameters.java b/src/org/bouncycastle/crypto/params/RSAKeyParameters.java
new file mode 100644 (file)
index 0000000..130678d
--- /dev/null
@@ -0,0 +1,34 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+
+public class RSAKeyParameters
+    extends AsymmetricKeyParameter
+{
+    private BigInteger      modulus;
+    private BigInteger      exponent;
+
+    public RSAKeyParameters(
+        boolean     isPrivate,
+        BigInteger  modulus,
+        BigInteger  exponent)
+    {
+        super(isPrivate);
+
+        this.modulus = modulus;
+        this.exponent = exponent;
+    }   
+
+    public BigInteger getModulus()
+    {
+        return modulus;
+    }
+
+    public BigInteger getExponent()
+    {
+        return exponent;
+    }
+}
diff --git a/src/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java b/src/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java
new file mode 100644 (file)
index 0000000..63d299c
--- /dev/null
@@ -0,0 +1,68 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+public class RSAPrivateCrtKeyParameters
+    extends RSAKeyParameters
+{
+    private BigInteger  e;
+    private BigInteger  p;
+    private BigInteger  q;
+    private BigInteger  dP;
+    private BigInteger  dQ;
+    private BigInteger  qInv;
+
+    /**
+     * 
+     */
+    public RSAPrivateCrtKeyParameters(
+        BigInteger  modulus,
+        BigInteger  publicExponent,
+        BigInteger  privateExponent,
+        BigInteger  p,
+        BigInteger  q,
+        BigInteger  dP,
+        BigInteger  dQ,
+        BigInteger  qInv)
+    {
+        super(true, modulus, privateExponent);
+
+        this.e = publicExponent;
+        this.p = p;
+        this.q = q;
+        this.dP = dP;
+        this.dQ = dQ;
+        this.qInv = qInv;
+    }
+
+    public BigInteger getPublicExponent()
+    {
+        return e;
+    }
+
+    public BigInteger getP()
+    {
+        return p;
+    }
+
+    public BigInteger getQ()
+    {
+        return q;
+    }
+
+    public BigInteger getDP()
+    {
+        return dP;
+    }
+
+    public BigInteger getDQ()
+    {
+        return dQ;
+    }
+
+    public BigInteger getQInv()
+    {
+        return qInv;
+    }
+}
diff --git a/src/org/bouncycastle/util/encoders/Base64.java b/src/org/bouncycastle/util/encoders/Base64.java
new file mode 100644 (file)
index 0000000..423fb30
--- /dev/null
@@ -0,0 +1,260 @@
+package org.bouncycastle.util.encoders;
+
+public class Base64
+{
+       private static byte[] encodingTable =
+               {
+                   (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+            (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+            (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+            (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+                   (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+            (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+            (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+            (byte)'v',
+                   (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+                   (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
+            (byte)'7', (byte)'8', (byte)'9',
+                   (byte)'+', (byte)'/'
+               };
+
+       /**
+        * encode the input data producong a base 64 encoded byte array.
+        *
+        * @return a byte array containing the base 64 encoded data.
+        */
+       public static byte[] encode(
+               byte[]  data)
+       {
+               byte[]  bytes;
+
+               if ((data.length % 3) == 0)
+               {
+                       bytes = new byte[4 * data.length / 3];
+               }
+               else
+               {
+                       bytes = new byte[4 * ((data.length / 3) + 1)];
+               }
+
+               for (int i = 0, j = 0;
+                               i < ((data.length / 3) * 3); i += 3, j += 4)
+               {
+                       int     b1, b2, b3, b4;
+                       int     d1, d2, d3;
+
+                       d1 = data[i] & 0xff;
+                       d2 = data[i + 1] & 0xff;
+                       d3 = data[i + 2] & 0xff;
+
+                       b1 = (d1 >>> 2) & 0x3f;
+                       b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
+                       b3 = ((d2 << 2) | (d3 >>> 6)) & 0x3f;
+                       b4 = d3 & 0x3f;
+
+                       bytes[j] = encodingTable[b1];
+                       bytes[j + 1] = encodingTable[b2];
+                       bytes[j + 2] = encodingTable[b3];
+                       bytes[j + 3] = encodingTable[b4];
+               }
+
+               /*
+                * process the tail end.
+                */
+               int     b1, b2, b3;
+               int     d1, d2;
+
+               switch (data.length % 3)
+               {
+               case 0:         /* nothing left to do */
+                       break;
+               case 1:
+                       d1 = data[data.length - 1] & 0xff;
+                       b1 = (d1 >>> 2) & 0x3f;
+                       b2 = (d1 << 4) & 0x3f;
+
+                       bytes[bytes.length - 4] = encodingTable[b1];
+                       bytes[bytes.length - 3] = encodingTable[b2];
+                       bytes[bytes.length - 2] = (byte)'=';
+                       bytes[bytes.length - 1] = (byte)'=';
+                       break;
+               case 2:
+                       d1 = data[data.length - 2] & 0xff;
+                       d2 = data[data.length - 1] & 0xff;
+
+                       b1 = (d1 >>> 2) & 0x3f;
+                       b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
+                       b3 = (d2 << 2) & 0x3f;
+
+                       bytes[bytes.length - 4] = encodingTable[b1];
+                       bytes[bytes.length - 3] = encodingTable[b2];
+                       bytes[bytes.length - 2] = encodingTable[b3];
+                       bytes[bytes.length - 1] = (byte)'=';
+                       break;
+               }
+
+               return bytes;
+       }
+
+       /*
+        * set up the decoding table.
+        */
+       private static byte[] decodingTable;
+
+       static
+       {
+               decodingTable = new byte[128];
+
+               for (int i = 'A'; i <= 'Z'; i++)
+               {
+                       decodingTable[i] = (byte)(i - 'A');
+               }
+
+               for (int i = 'a'; i <= 'z'; i++)
+               {
+                       decodingTable[i] = (byte)(i - 'a' + 26);
+               }
+
+               for (int i = '0'; i <= '9'; i++)
+               {
+                       decodingTable[i] = (byte)(i - '0' + 52);
+               }
+
+               decodingTable['+'] = 62;
+               decodingTable['/'] = 63;
+       }
+
+       /**
+        * decode the base 64 encoded input data.
+        *
+        * @return a byte array representing the decoded data.
+        */
+       public static byte[] decode(
+               byte[]  data)
+       {
+               byte[]  bytes;
+               byte    b1, b2, b3, b4;
+
+               if (data[data.length - 2] == '=')
+               {
+                       bytes = new byte[(((data.length / 4) - 1) * 3) + 1];
+               }
+               else if (data[data.length - 1] == '=')
+               {
+                       bytes = new byte[(((data.length / 4) - 1) * 3) + 2];
+               }
+               else
+               {
+                       bytes = new byte[((data.length / 4) * 3)];
+               }
+
+               for (int i = 0, j = 0; i < data.length - 4; i += 4, j += 3)
+               {
+                       b1 = decodingTable[data[i]];
+                       b2 = decodingTable[data[i + 1]];
+                       b3 = decodingTable[data[i + 2]];
+                       b4 = decodingTable[data[i + 3]];
+
+                       bytes[j] = (byte)((b1 << 2) | (b2 >> 4));
+                       bytes[j + 1] = (byte)((b2 << 4) | (b3 >> 2));
+                       bytes[j + 2] = (byte)((b3 << 6) | b4);
+               }
+
+               if (data[data.length - 2] == '=')
+               {
+                       b1 = decodingTable[data[data.length - 4]];
+                       b2 = decodingTable[data[data.length - 3]];
+
+                       bytes[bytes.length - 1] = (byte)((b1 << 2) | (b2 >> 4));
+               }
+               else if (data[data.length - 1] == '=')
+               {
+                       b1 = decodingTable[data[data.length - 4]];
+                       b2 = decodingTable[data[data.length - 3]];
+                       b3 = decodingTable[data[data.length - 2]];
+
+                       bytes[bytes.length - 2] = (byte)((b1 << 2) | (b2 >> 4));
+                       bytes[bytes.length - 1] = (byte)((b2 << 4) | (b3 >> 2));
+               }
+               else
+               {
+                       b1 = decodingTable[data[data.length - 4]];
+                       b2 = decodingTable[data[data.length - 3]];
+                       b3 = decodingTable[data[data.length - 2]];
+                       b4 = decodingTable[data[data.length - 1]];
+
+                       bytes[bytes.length - 3] = (byte)((b1 << 2) | (b2 >> 4));
+                       bytes[bytes.length - 2] = (byte)((b2 << 4) | (b3 >> 2));
+                       bytes[bytes.length - 1] = (byte)((b3 << 6) | b4);
+               }
+
+               return bytes;
+       }
+
+       /**
+        * decode the base 64 encoded String data.
+        *
+        * @return a byte array representing the decoded data.
+        */
+       public static byte[] decode(
+               String  data)
+       {
+               byte[]  bytes;
+               byte    b1, b2, b3, b4;
+
+               if (data.charAt(data.length() - 2) == '=')
+               {
+                       bytes = new byte[(((data.length() / 4) - 1) * 3) + 1];
+               }
+               else if (data.charAt(data.length() - 1) == '=')
+               {
+                       bytes = new byte[(((data.length() / 4) - 1) * 3) + 2];
+               }
+               else
+               {
+                       bytes = new byte[((data.length() / 4) * 3)];
+               }
+
+               for (int i = 0, j = 0; i < data.length() - 4; i += 4, j += 3)
+               {
+                       b1 = decodingTable[data.charAt(i)];
+                       b2 = decodingTable[data.charAt(i + 1)];
+                       b3 = decodingTable[data.charAt(i + 2)];
+                       b4 = decodingTable[data.charAt(i + 3)];
+
+                       bytes[j] = (byte)((b1 << 2) | (b2 >> 4));
+                       bytes[j + 1] = (byte)((b2 << 4) | (b3 >> 2));
+                       bytes[j + 2] = (byte)((b3 << 6) | b4);
+               }
+
+               if (data.charAt(data.length() - 2) == '=')
+               {
+                       b1 = decodingTable[data.charAt(data.length() - 4)];
+                       b2 = decodingTable[data.charAt(data.length() - 3)];
+
+                       bytes[bytes.length - 1] = (byte)((b1 << 2) | (b2 >> 4));
+               }
+               else if (data.charAt(data.length() - 1) == '=')
+               {
+                       b1 = decodingTable[data.charAt(data.length() - 4)];
+                       b2 = decodingTable[data.charAt(data.length() - 3)];
+                       b3 = decodingTable[data.charAt(data.length() - 2)];
+
+                       bytes[bytes.length - 2] = (byte)((b1 << 2) | (b2 >> 4));
+                       bytes[bytes.length - 1] = (byte)((b2 << 4) | (b3 >> 2));
+               }
+               else
+               {
+                       b1 = decodingTable[data.charAt(data.length() - 4)];
+                       b2 = decodingTable[data.charAt(data.length() - 3)];
+                       b3 = decodingTable[data.charAt(data.length() - 2)];
+                       b4 = decodingTable[data.charAt(data.length() - 1)];
+
+                       bytes[bytes.length - 3] = (byte)((b1 << 2) | (b2 >> 4));
+                       bytes[bytes.length - 2] = (byte)((b2 << 4) | (b3 >> 2));
+                       bytes[bytes.length - 1] = (byte)((b3 << 6) | b4);
+               }
+
+               return bytes;
+       }
+}
diff --git a/src/org/mozilla/javascript/Arguments.java b/src/org/mozilla/javascript/Arguments.java
new file mode 100644 (file)
index 0000000..66da72d
--- /dev/null
@@ -0,0 +1,146 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This class implements the "arguments" object.\r
+ *\r
+ * See ECMA 10.1.8\r
+ *\r
+ * @see org.mozilla.javascript.NativeCall\r
+ * @author Norris Boyd\r
+ */\r
+class Arguments extends ScriptableObject {\r
+\r
+    public Arguments(NativeCall activation) {\r
+        this.activation = activation;\r
+\r
+        Scriptable parent = activation.getParentScope();\r
+        setParentScope(parent);\r
+        setPrototype(ScriptableObject.getObjectPrototype(parent));\r
+\r
+        args = activation.getOriginalArguments();\r
+        int length = args.length;\r
+        Object callee = activation.funObj;\r
+\r
+        defineProperty("length", new Integer(length),\r
+                       ScriptableObject.DONTENUM);\r
+        defineProperty("callee", callee, ScriptableObject.DONTENUM);\r
+\r
+        hasCaller = (activation.funObj.version <= Context.VERSION_1_3 &&\r
+                     activation.funObj.version != Context.VERSION_DEFAULT);\r
+    }\r
+\r
+    public String getClassName() {\r
+        return "Arguments";\r
+    }\r
+\r
+    public boolean has(String name, Scriptable start) {\r
+        return (hasCaller && name.equals("caller")) || super.has(name, start);\r
+    }\r
+\r
+    public boolean has(int index, Scriptable start) {\r
+        Object[] args = activation.getOriginalArguments();\r
+        return (0 <= index && index < args.length) || super.has(index, start);\r
+    }\r
+\r
+    public Object get(String name, Scriptable start) {\r
+        if (hasCaller && name.equals("caller")) {\r
+            NativeCall caller = activation.caller;\r
+            if (caller == null || caller.originalArgs == null) return null;\r
+            return caller.get("arguments", caller);\r
+\r
+        } else if (name.equals("cascade")) {\r
+            return org.xwt.Trap.cascadeFunction;\r
+\r
+        } else if (name.equals("trapee")) {\r
+            return org.xwt.Trap.currentTrapee();\r
+        }\r
+\r
+        return super.get(name, start);\r
+    }\r
+\r
+    public Object get(int index, Scriptable start) {\r
+        if (0 <= index && index < args.length) {\r
+            NativeFunction f = activation.funObj;\r
+            if (index < f.argCount)\r
+                return activation.get(f.argNames[index], activation);\r
+            return args[index];\r
+        }\r
+        return super.get(index, start);\r
+    }\r
+\r
+    public void put(String name, Scriptable start, Object value) {\r
+        if (name.equals("caller")) {\r
+            // Set "hasCaller" to false so that we won't look up a  \r
+            // computed value.\r
+            hasCaller = false;\r
+        }\r
+        super.put(name, start, value);\r
+    }\r
+\r
+    public void put(int index, Scriptable start, Object value) {\r
+        if (0 <= index && index < args.length) {\r
+            NativeFunction f = activation.funObj;\r
+            if (index < f.argCount)\r
+                activation.put(f.argNames[index], activation, value);\r
+            else\r
+                args[index] = value;\r
+            return;\r
+        }\r
+        super.put(index, start, value);\r
+    }\r
+\r
+    public void delete(String name) {\r
+        if (name.equals("caller"))\r
+            hasCaller = false;\r
+        super.delete(name);\r
+    }\r
+\r
+    public void delete(int index) {\r
+        if (0 <= index && index < args.length) {\r
+            NativeFunction f = activation.funObj;\r
+            if (index < f.argCount)\r
+                activation.delete(f.argNames[index]);\r
+            else\r
+                args[index] = Undefined.instance;\r
+        }\r
+    }\r
+\r
+    private NativeCall activation;\r
+    private Object[] args;\r
+    private boolean hasCaller;\r
+}\r
diff --git a/src/org/mozilla/javascript/BaseFunction.java b/src/org/mozilla/javascript/BaseFunction.java
new file mode 100644 (file)
index 0000000..79c4f36
--- /dev/null
@@ -0,0 +1,521 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Igor Bukanov\r
+ * Roger Lawrence\r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * The base class for Function objects\r
+ * See ECMA 15.3.\r
+ * @author Norris Boyd\r
+ */\r
+public class BaseFunction extends IdScriptable implements Function {\r
+\r
+    static void init(Context cx, Scriptable scope, boolean sealed) {\r
+        BaseFunction obj = new BaseFunction();\r
+        obj.prototypeFlag = true;\r
+        obj.functionName = "";\r
+        obj.prototypePropertyAttrs = DONTENUM | READONLY | PERMANENT;\r
+        obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);\r
+    }\r
+    \r
+    protected void fillConstructorProperties\r
+        (Context cx, IdFunction ctor, boolean sealed)\r
+    {\r
+        // Fix up bootstrapping problem: getPrototype of the IdFunction \r
+        // can not return Function.prototype because Function object is not\r
+        // yet defined.\r
+        ctor.setPrototype(this);\r
+    }\r
+\r
+    public String getClassName() {\r
+        return "Function";\r
+    }\r
+\r
+    /**\r
+     * Implements the instanceof operator for JavaScript Function objects.\r
+     * <p>\r
+     * <code>\r
+     * foo = new Foo();<br>\r
+     * foo instanceof Foo;  // true<br>\r
+     * </code>\r
+     *\r
+     * @param instance The value that appeared on the LHS of the instanceof\r
+     *              operator\r
+     * @return true if the "prototype" property of "this" appears in\r
+     *              value's prototype chain\r
+     *\r
+     */\r
+    public boolean hasInstance(Scriptable instance) {\r
+        Object protoProp = ScriptableObject.getProperty(this, "prototype");\r
+        if (protoProp instanceof Scriptable && protoProp != Undefined.instance)\r
+        {\r
+            return ScriptRuntime.jsDelegatesTo(instance, (Scriptable)protoProp);\r
+        }\r
+        throw NativeGlobal.typeError1\r
+            ("msg.instanceof.bad.prototype", functionName, instance);\r
+    }\r
+\r
+    protected int getIdDefaultAttributes(int id) {\r
+        switch (id) {\r
+            case Id_length:\r
+            case Id_arity:\r
+            case Id_name:\r
+                return DONTENUM | READONLY | PERMANENT;\r
+            case Id_prototype:\r
+                return prototypePropertyAttrs;\r
+            case Id_arguments:\r
+                return EMPTY;\r
+        }\r
+        return super.getIdDefaultAttributes(id);\r
+    }\r
+    \r
+    protected boolean hasIdValue(int id) {\r
+        if (id == Id_prototype) {\r
+            return prototypeProperty != NOT_FOUND;\r
+        }\r
+        else if (id == Id_arguments) {\r
+            // Should after delete Function.arguments its activation still\r
+            // be available during Function call? \r
+            // This code assumes it should not: after default set/deleteIdValue\r
+            // hasIdValue/getIdValue would not be called again\r
+            // To handle the opposite case, set/deleteIdValue should be \r
+            // overwritten as well\r
+            return null != getActivation(Context.getContext());\r
+        }\r
+        return super.hasIdValue(id);\r
+    }\r
+    \r
+    protected Object getIdValue(int id) {\r
+        switch (id) {\r
+            case Id_length:    return wrap_int(getLength());\r
+            case Id_arity:     return wrap_int(getArity());\r
+            case Id_name:      return getFunctionName();\r
+            case Id_prototype: return getPrototypeProperty();\r
+            case Id_arguments: return getArguments();\r
+        }\r
+        return super.getIdValue(id);\r
+    }\r
+    \r
+    protected void setIdValue(int id, Object value) {\r
+        if (id == Id_prototype) {\r
+            prototypeProperty = (value != null) ? value : NULL_TAG;\r
+            return;\r
+        }\r
+        super.setIdValue(id, value);\r
+    }\r
+\r
+    protected void deleteIdValue(int id) {\r
+        if (id == Id_prototype) {\r
+            prototypeProperty = NOT_FOUND;\r
+            return;\r
+        }\r
+        super.deleteIdValue(id);\r
+    }\r
+\r
+    public int methodArity(int methodId) {\r
+        if (prototypeFlag) {\r
+            switch (methodId) {\r
+                case Id_constructor:\r
+                case Id_toString:\r
+                case Id_apply:\r
+                case Id_call:\r
+                    return 1;\r
+            }\r
+        }\r
+        return super.methodArity(methodId);\r
+    }\r
+\r
+    public Object execMethod(int methodId, IdFunction f, Context cx,\r
+                             Scriptable scope, Scriptable thisObj, \r
+                             Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (prototypeFlag) {\r
+            switch (methodId) {\r
+                case Id_constructor:\r
+                    return jsConstructor(cx, scope, args);\r
+\r
+                case Id_toString:\r
+                    return jsFunction_toString(cx, thisObj, args);\r
+\r
+                case Id_apply:\r
+                    return jsFunction_apply(cx, scope, thisObj, args);\r
+\r
+                case Id_call:\r
+                    return jsFunction_call(cx, scope, thisObj, args);\r
+            }\r
+        }\r
+        return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
+    }\r
+\r
+    /**\r
+     * Make value as DontEnum, DontDelete, ReadOnly\r
+     * prototype property of this Function object \r
+     */\r
+    public void setImmunePrototypeProperty(Object value) {\r
+        prototypeProperty = (value != null) ? value : NULL_TAG;\r
+        prototypePropertyAttrs = DONTENUM | READONLY | PERMANENT;\r
+    }\r
+\r
+    protected Scriptable getClassPrototype() {\r
+        Object protoVal = getPrototypeProperty();\r
+        if (protoVal == null \r
+                    || !(protoVal instanceof Scriptable)\r
+                    || (protoVal == Undefined.instance))\r
+            protoVal = getClassPrototype(this, "Object");\r
+        return (Scriptable) protoVal;\r
+    }\r
+\r
+    /**\r
+     * Should be overridden.\r
+     */\r
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj,\r
+                       Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        return Undefined.instance;\r
+    }\r
+\r
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        Scriptable newInstance = new NativeObject();\r
+\r
+        newInstance.setPrototype(getClassPrototype());\r
+        newInstance.setParentScope(getParentScope());\r
+\r
+        Object val = call(cx, scope, newInstance, args);\r
+        if (val instanceof Scriptable && val != Undefined.instance) {\r
+            return (Scriptable) val;\r
+        }\r
+        return newInstance;\r
+    }\r
+\r
+    /**\r
+     * Decompile the source information associated with this js\r
+     * function/script back into a string.\r
+     *\r
+     * @param cx Current context\r
+     *\r
+     * @param indent How much to indent the decompiled result\r
+     *\r
+     * @param justbody Whether the decompilation should omit the\r
+     * function header and trailing brace.\r
+     */\r
+\r
+    public String decompile(Context cx, int indent, boolean justbody) {\r
+        StringBuffer sb = new StringBuffer();\r
+        if (!justbody) {\r
+            sb.append("function ");\r
+            sb.append(getFunctionName());\r
+            sb.append("() {\n\t");\r
+        }\r
+        sb.append("[native code, arity=");\r
+        sb.append(getArity());\r
+        sb.append("]\n");\r
+        if (!justbody) {\r
+            sb.append("}\n");\r
+        }\r
+        return sb.toString();\r
+    }\r
+\r
+    public int getArity() { return 0; }\r
+\r
+    public int getLength() { return 0; }\r
+\r
+    public String getFunctionName() {\r
+        if (functionName == null)\r
+            return "";\r
+        if (functionName.equals("anonymous")) {\r
+            Context cx = Context.getCurrentContext();\r
+            if (cx != null && cx.getLanguageVersion() == Context.VERSION_1_2)\r
+                return "";\r
+        }\r
+        return functionName;\r
+    }\r
+\r
+    private Object getPrototypeProperty() {\r
+        Object result = prototypeProperty;\r
+        if (result == null) { \r
+            synchronized (this) {\r
+                result = prototypeProperty;\r
+                if (result == null) {\r
+                    setupDefaultPrototype();\r
+                    result = prototypeProperty;\r
+                }\r
+            }\r
+        }\r
+        else if (result == NULL_TAG) { result = null; }\r
+        return result;\r
+    }\r
+\r
+    private void setupDefaultPrototype() {\r
+        NativeObject obj = new NativeObject();\r
+        final int attr = ScriptableObject.DONTENUM |\r
+                         ScriptableObject.READONLY |\r
+                         ScriptableObject.PERMANENT;\r
+        obj.defineProperty("constructor", this, attr);\r
+        // put the prototype property into the object now, then in the\r
+        // wacky case of a user defining a function Object(), we don't\r
+        // get an infinite loop trying to find the prototype.\r
+        prototypeProperty = obj;\r
+        Scriptable proto = getObjectPrototype(this);            \r
+        if (proto != obj) {\r
+            // not the one we just made, it must remain grounded\r
+            obj.setPrototype(proto);\r
+        }\r
+    }\r
+\r
+    private Object getArguments() {\r
+        // <Function name>.arguments is deprecated, so we use a slow\r
+        // way of getting it that doesn't add to the invocation cost.\r
+        // TODO: add warning, error based on version\r
+        NativeCall activation = getActivation(Context.getContext());\r
+        return activation == null \r
+               ? null \r
+               : activation.get("arguments", activation);\r
+    }\r
+    \r
+    NativeCall getActivation(Context cx) {\r
+        NativeCall activation = cx.currentActivation;\r
+        while (activation != null) {\r
+            if (activation.getFunctionObject() == this) \r
+                return activation;\r
+            activation = activation.caller;\r
+        }\r
+        return null;\r
+    }\r
+        \r
+    private static Object jsConstructor(Context cx, Scriptable scope, \r
+                                        Object[] args)\r
+    {\r
+        int arglen = args.length;\r
+        StringBuffer funArgs = new StringBuffer();\r
+\r
+        /* Collect the arguments into a string.  */\r
+\r
+        int i;\r
+        for (i = 0; i < arglen - 1; i++) {\r
+            if (i > 0)\r
+                funArgs.append(',');\r
+            funArgs.append(ScriptRuntime.toString(args[i]));\r
+        }\r
+        String funBody = arglen == 0 ? "" : ScriptRuntime.toString(args[i]);\r
+\r
+        String source = "function (" + funArgs.toString() + ") {" +\r
+                        funBody + "}";\r
+        int[] linep = { 0 };\r
+        String filename = Context.getSourcePositionFromStack(linep);\r
+        if (filename == null) {\r
+            filename = "<eval'ed string>";\r
+            linep[0] = 1;\r
+        }\r
+        Object securityDomain = cx.getSecurityDomainForStackDepth(4);\r
+        Scriptable global = ScriptableObject.getTopLevelScope(scope);\r
+        \r
+        // Compile the function with opt level of -1 to force interpreter\r
+        // mode.\r
+        int oldOptLevel = cx.getOptimizationLevel();\r
+        cx.setOptimizationLevel(-1);\r
+        NativeFunction fn;\r
+        try {\r
+            fn = (NativeFunction) cx.compileFunction(global, source,\r
+                                                     filename, linep[0], \r
+                                                     securityDomain);\r
+        }\r
+        finally { cx.setOptimizationLevel(oldOptLevel); }\r
+\r
+        fn.functionName = "anonymous";\r
+        fn.setPrototype(getFunctionPrototype(global));\r
+        fn.setParentScope(global);\r
+\r
+        return fn;\r
+    }\r
+\r
+    private static Object jsFunction_toString(Context cx, Scriptable thisObj,\r
+                                              Object[] args)\r
+    {\r
+        int indent = ScriptRuntime.toInt32(args, 0);\r
+        Object val = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);\r
+        if (val instanceof BaseFunction) {\r
+            return ((BaseFunction)val).decompile(cx, indent, false);\r
+        }\r
+        throw NativeGlobal.typeError1("msg.incompat.call", "toString", thisObj);\r
+    }\r
+\r
+    /**\r
+     * Function.prototype.apply\r
+     *\r
+     * A proposed ECMA extension for round 2.\r
+     */\r
+    private static Object jsFunction_apply(Context cx, Scriptable scope,\r
+                                           Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (args.length != 2)\r
+            return jsFunction_call(cx, scope, thisObj, args);\r
+        Object val = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);\r
+        Scriptable newThis = args[0] == null\r
+                             ? ScriptableObject.getTopLevelScope(thisObj)\r
+                             : ScriptRuntime.toObject(scope, args[0]);\r
+        Object[] newArgs;\r
+        if (args.length > 1) {\r
+            if ((args[1] instanceof NativeArray) \r
+                    || (args[1] instanceof Arguments))\r
+                newArgs = cx.getElements((Scriptable) args[1]);\r
+            else\r
+                throw NativeGlobal.typeError0("msg.arg.isnt.array", thisObj);            \r
+        }\r
+        else\r
+            newArgs = ScriptRuntime.emptyArgs;\r
+        return ScriptRuntime.call(cx, val, newThis, newArgs, newThis);\r
+    }\r
+\r
+    /**\r
+     * Function.prototype.call\r
+     *\r
+     * A proposed ECMA extension for round 2.\r
+     */\r
+    private static Object jsFunction_call(Context cx, Scriptable scope,\r
+                                          Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        Object val = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);\r
+        if (args.length == 0) {\r
+            Scriptable s = ScriptRuntime.toObject(scope, val);\r
+            Scriptable topScope = s.getParentScope();\r
+            return ScriptRuntime.call(cx, val, \r
+                                      topScope, ScriptRuntime.emptyArgs, \r
+                                      topScope);\r
+        } else {\r
+            Scriptable newThis = args[0] == null\r
+                                 ? ScriptableObject.getTopLevelScope(thisObj)\r
+                                 : ScriptRuntime.toObject(scope, args[0]);\r
+\r
+            Object[] newArgs = new Object[args.length - 1];\r
+            System.arraycopy(args, 1, newArgs, 0, newArgs.length);\r
+            return ScriptRuntime.call(cx, val, newThis, newArgs, newThis);\r
+        }\r
+    }\r
+\r
+    protected int maxInstanceId() { return MAX_INSTANCE_ID; }\r
+\r
+    protected String getIdName(int id) {\r
+        switch (id) {\r
+            case Id_length:       return "length";\r
+            case Id_arity:        return "arity";\r
+            case Id_name:         return "name";\r
+            case Id_prototype:    return "prototype";\r
+            case Id_arguments:    return "arguments";\r
+        }\r
+        \r
+        if (prototypeFlag) {\r
+            switch (id) {\r
+                case Id_constructor:  return "constructor";\r
+                case Id_toString:     return "toString";\r
+                case Id_apply:        return "apply";\r
+                case Id_call:         return "call";\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+// #string_id_map#\r
+\r
+    private static final int\r
+        Id_length       = 1,\r
+        Id_arity        = 2,\r
+        Id_name         = 3,\r
+        Id_prototype    = 4,\r
+        Id_arguments    = 5,\r
+        \r
+        MAX_INSTANCE_ID = 5;\r
+\r
+    protected int mapNameToId(String s) {\r
+        int id;\r
+// #generated# Last update: 2001-05-20 00:12:12 GMT+02:00\r
+        L0: { id = 0; String X = null; int c;\r
+            L: switch (s.length()) {\r
+            case 4: X="name";id=Id_name; break L;\r
+            case 5: X="arity";id=Id_arity; break L;\r
+            case 6: X="length";id=Id_length; break L;\r
+            case 9: c=s.charAt(0);\r
+                if (c=='a') { X="arguments";id=Id_arguments; }\r
+                else if (c=='p') { X="prototype";id=Id_prototype; }\r
+                break L;\r
+            }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+// #/string_id_map#\r
+\r
+        if (id != 0 || !prototypeFlag) { return id; }\r
+\r
+// #string_id_map#\r
+// #generated# Last update: 2001-05-20 00:12:12 GMT+02:00\r
+        L0: { id = 0; String X = null;\r
+            L: switch (s.length()) {\r
+            case 4: X="call";id=Id_call; break L;\r
+            case 5: X="apply";id=Id_apply; break L;\r
+            case 8: X="toString";id=Id_toString; break L;\r
+            case 11: X="constructor";id=Id_constructor; break L;\r
+            }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+        return id;\r
+    }\r
+\r
+    private static final int\r
+        Id_constructor    = MAX_INSTANCE_ID + 1,\r
+        Id_toString       = MAX_INSTANCE_ID + 2,\r
+        Id_apply          = MAX_INSTANCE_ID + 3,\r
+        Id_call           = MAX_INSTANCE_ID + 4,\r
+        \r
+        MAX_PROTOTYPE_ID  = MAX_INSTANCE_ID + 4;\r
+\r
+// #/string_id_map#\r
+        \r
+    protected String functionName;\r
+\r
+    private Object prototypeProperty; \r
+    private int prototypePropertyAttrs = DONTENUM;\r
+\r
+    private boolean prototypeFlag;\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/BinaryDigitReader.java b/src/org/mozilla/javascript/BinaryDigitReader.java
new file mode 100644 (file)
index 0000000..a6b4771
--- /dev/null
@@ -0,0 +1,75 @@
+\r
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Waldemar Horwat\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+package org.mozilla.javascript;\r
+\r
+final class BinaryDigitReader {\r
+    int lgBase;                        // Logarithm of base of number\r
+    int digit;                 // Current digit value in radix given by base\r
+    int digitPos;              // Bit position of last bit extracted from digit\r
+    String digits;             // String containing the digits\r
+    int start;                 // Index of the first remaining digit\r
+    int end;                   // Index past the last remaining digit\r
+\r
+    BinaryDigitReader(int base, String digits, int start, int end) {\r
+        lgBase = 0;\r
+        while (base != 1) {\r
+            lgBase++;\r
+            base >>= 1;\r
+        }\r
+        digitPos = 0;\r
+        this.digits = digits;\r
+        this.start = start;\r
+        this.end = end;\r
+    }\r
+\r
+    /* Return the next binary digit from the number or -1 if done */\r
+    int getNextBinaryDigit()\r
+    {\r
+        if (digitPos == 0) {\r
+            if (start == end)\r
+                return -1;\r
+    \r
+            char c = digits.charAt(start++);\r
+            if ('0' <= c && c <= '9')\r
+                digit = c - '0';\r
+            else if ('a' <= c && c <= 'z')\r
+                digit = c - 'a' + 10;\r
+            else digit = c - 'A' + 10;\r
+            digitPos = lgBase;\r
+        }\r
+        return digit >> --digitPos & 1;\r
+    }\r
+}\r
diff --git a/src/org/mozilla/javascript/ClassDefinitionException.java b/src/org/mozilla/javascript/ClassDefinitionException.java
new file mode 100644 (file)
index 0000000..3b5a8e7
--- /dev/null
@@ -0,0 +1,48 @@
+\r
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * Thrown if errors are detected while attempting to define a host object\r
+ * from a Java class.\r
+ */\r
+public class ClassDefinitionException extends Exception {\r
+\r
+    public ClassDefinitionException(String detail) {\r
+        super(detail);\r
+    }\r
+}\r
diff --git a/src/org/mozilla/javascript/ClassNameHelper.java b/src/org/mozilla/javascript/ClassNameHelper.java
new file mode 100644 (file)
index 0000000..70cb600
--- /dev/null
@@ -0,0 +1,63 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Norris Boyd\r
+ * Roger Lawrence\r
+ * Andi Vajda\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+public interface ClassNameHelper {\r
+\r
+    public String getTargetClassFileName();\r
+\r
+    public void setTargetClassFileName(String classFileName);\r
+\r
+    public String getTargetPackage();\r
+\r
+    public void setTargetPackage(String targetPackage);\r
+\r
+    public String getTargetClassFileName(String className);\r
+    \r
+    public String getGeneratingDirectory();\r
+    \r
+    public void setTargetExtends(Class extendsClass);\r
+    \r
+    public void setTargetImplements(Class[] implementsClasses);\r
+\r
+    public ClassOutput getClassOutput();\r
+\r
+    public void setClassOutput(ClassOutput classOutput);\r
+    \r
+    public void reset();\r
+}\r
diff --git a/src/org/mozilla/javascript/ClassOutput.java b/src/org/mozilla/javascript/ClassOutput.java
new file mode 100644 (file)
index 0000000..997b89e
--- /dev/null
@@ -0,0 +1,57 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Andi Vajda\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+package org.mozilla.javascript;\r
+\r
+// API class\r
+\r
+import java.io.*;\r
+\r
+/**\r
+ * This interface is implemented by classes interested in the bytecode\r
+ * generated by the rhino compiler for script objects.\r
+ *\r
+ * @see Context\r
+ * @author Andi Vajda\r
+ */\r
+public interface ClassOutput {\r  \r
+    /**\r
+     * @param className the name of the class for which bytecode is ready.\r
+     * @param isTopLevel if true, represents the top-level script being compiled\r
+     * @return a stream into which to write bytecode.\r
+     * @since 1.5 Release 2\r
+     */\r
+    public OutputStream getOutputStream(String className, boolean isTopLevel)\r
+        throws IOException;\r
+}\r
diff --git a/src/org/mozilla/javascript/Context.java b/src/org/mozilla/javascript/Context.java
new file mode 100644 (file)
index 0000000..6e6dfbb
--- /dev/null
@@ -0,0 +1,2201 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ *\r
+ * Patrick Beard\r
+ * Norris Boyd\r
+ * Igor Bukanov\r
+ * Brendan Eich\r
+ * Roger Lawrence\r
+ * Mike McCabe\r
+ * Ian D. Stewart\r
+ * Andi Vajda\r
+ * Andrew Wason\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.beans.*;\r
+import java.io.*;\r
+import java.util.Vector;\r
+import java.util.Enumeration;\r
+import java.util.Hashtable;\r
+import java.util.Locale;\r
+import java.util.ResourceBundle;\r
+import java.util.ListResourceBundle;\r
+import java.text.MessageFormat;\r
+import java.lang.reflect.*;\r
+import org.mozilla.javascript.debug.*;\r
+\r
+/**\r
+ * This class represents the runtime context of an executing script.\r
+ *\r
+ * Before executing a script, an instance of Context must be created\r
+ * and associated with the thread that will be executing the script.\r
+ * The Context will be used to store information about the executing\r
+ * of the script such as the call stack. Contexts are associated with\r
+ * the current thread  using the <a href="#enter()">enter()</a> method.<p>\r
+ *\r
+ * The behavior of the execution engine may be altered through methods\r
+ * such as <a href="#setLanguageVersion>setLanguageVersion</a> and\r
+ * <a href="#setErrorReporter>setErrorReporter</a>.<p>\r
+ *\r
+ * Different forms of script execution are supported. Scripts may be\r
+ * evaluated from the source directly, or first compiled and then later\r
+ * executed. Interactive execution is also supported.<p>\r
+ *\r
+ * Some aspects of script execution, such as type conversions and\r
+ * object creation, may be accessed directly through methods of\r
+ * Context.\r
+ *\r
+ * @see Scriptable\r
+ * @author Norris Boyd\r
+ * @author Brendan Eich\r
+ */\r
+\r
+public class Context {\r
+    public static final String languageVersionProperty = "language version";\r
+    public static final String errorReporterProperty   = "error reporter";\r
+    \r
+    /**\r
+     * Create a new Context.\r
+     *\r
+     * Note that the Context must be associated with a thread before\r
+     * it can be used to execute a script.\r
+     *\r
+     * @see org.mozilla.javascript.Context#enter\r
+     */\r
+    public Context() {\r
+        init();\r
+    }\r
+    \r
+    /**\r
+     * Create a new context with the associated security support.\r
+     * \r
+     * @param securitySupport an encapsulation of the functionality \r
+     *        needed to support security for scripts.\r
+     * @see org.mozilla.javascript.SecuritySupport\r
+     */\r
+    public Context(SecuritySupport securitySupport) {\r
+        this.securitySupport = securitySupport;\r
+        init();\r
+    }\r
+    \r
+    private void init() {\r
+        setLanguageVersion(VERSION_DEFAULT);\r
+        optimizationLevel = codegenClass != null ? 0 : -1;\r
+        Object[] array = contextListeners;\r
+        if (array != null) {\r
+            for (int i = array.length; i-- != 0;) {\r
+                ((ContextListener)array[i]).contextCreated(this);\r
+            }\r
+        }\r
+    }\r
+        \r
+    /**\r
+     * Get a context associated with the current thread, creating\r
+     * one if need be.\r
+     *\r
+     * The Context stores the execution state of the JavaScript\r
+     * engine, so it is required that the context be entered\r
+     * before execution may begin. Once a thread has entered\r
+     * a Context, then getCurrentContext() may be called to find\r
+     * the context that is associated with the current thread.\r
+     * <p>\r
+     * Calling <code>enter()</code> will\r
+     * return either the Context currently associated with the\r
+     * thread, or will create a new context and associate it \r
+     * with the current thread. Each call to <code>enter()</code>\r
+     * must have a matching call to <code>exit()</code>. For example,\r
+     * <pre>\r
+     *      Context cx = Context.enter();\r
+     *      ...\r
+     *      cx.evaluateString(...);\r
+     *      Context.exit();\r
+     * </pre>\r
+     * @return a Context associated with the current thread\r
+     * @see org.mozilla.javascript.Context#getCurrentContext\r
+     * @see org.mozilla.javascript.Context#exit\r
+     */\r
+    public static Context enter() {\r
+        return enter(null);\r
+    }\r
+    \r
+    /**\r
+     * Get a Context associated with the current thread, using\r
+     * the given Context if need be.\r
+     * <p>\r
+     * The same as <code>enter()</code> except that <code>cx</code>\r
+     * is associated with the current thread and returned if \r
+     * the current thread has no associated context and <code>cx</code>\r
+     * is not associated with any other thread.\r
+     * @param cx a Context to associate with the thread if possible\r
+     * @return a Context associated with the current thread\r
+     */\r
+    public static Context enter(Context cx) {\r
+        // There's some duplication of code in this method to avoid\r
+        // unnecessary synchronizations.\r
+        Thread t = Thread.currentThread();\r
+        Context current = (Context) threadContexts.get(t);\r
+        if (current != null) {\r
+            synchronized (current) {\r
+                current.enterCount++;\r
+            }\r
+        }\r
+        else if (cx != null) {\r
+            synchronized (cx) {\r
+                if (cx.currentThread == null) {\r
+                    cx.currentThread = t;\r
+                    threadContexts.put(t, cx);\r
+                    cx.enterCount++;\r
+                }\r
+            }\r
+            current = cx;\r
+        }\r
+        else {\r
+        current = new Context();\r
+        current.currentThread = t;\r
+        threadContexts.put(t, current);\r
+        current.enterCount = 1;\r
+        }\r
+        Object[] array = contextListeners;\r
+        if (array != null) {\r
+            for (int i = array.length; i-- != 0;) {\r
+                ((ContextListener)array[i]).contextEntered(current);\r
+            }\r
+        }\r
+        return current;\r
+     }\r
+        \r
+    /**\r
+     * Exit a block of code requiring a Context.\r
+     *\r
+     * Calling <code>exit()</code> will remove the association between\r
+     * the current thread and a Context if the prior call to \r
+     * <code>enter()</code> on this thread newly associated a Context \r
+     * with this thread.\r
+     * Once the current thread no longer has an associated Context,\r
+     * it cannot be used to execute JavaScript until it is again associated\r
+     * with a Context.\r
+     *\r
+     * @see org.mozilla.javascript.Context#enter\r
+     */\r
+    public static void exit() {\r
+        Context cx = getCurrentContext();\r
+        boolean released = false;\r
+        if (cx != null) {\r
+            synchronized (cx) {\r
+                if (--cx.enterCount == 0) {\r
+                    threadContexts.remove(cx.currentThread);\r
+                    cx.currentThread = null;\r
+                    released = true;\r
+                }\r
+            }\r
+            Object[] array = contextListeners;\r
+            if (array != null) {\r
+                for (int i = array.length; i-- != 0;) {\r
+                    ContextListener l = (ContextListener)array[i];\r
+                    l.contextExited(cx);\r
+                    if (released) { l.contextReleased(cx); }\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Get the current Context.\r
+     *\r
+     * The current Context is per-thread; this method looks up\r
+     * the Context associated with the current thread. <p>\r
+     *\r
+     * @return the Context associated with the current thread, or\r
+     *         null if no context is associated with the current \r
+     *         thread.\r
+     * @see org.mozilla.javascript.Context#enter\r
+     * @see org.mozilla.javascript.Context#exit\r
+     */\r
+    public static Context getCurrentContext() {\r
+        Thread t = Thread.currentThread();\r
+        return (Context) threadContexts.get(t);\r
+    }\r
+    \r
+    public static Context getContextForThread(Thread t) {\r
+        Context ret = (Context) threadContexts.get(t);\r
+        return ret == null ? Context.enter() : ret;\r
+    }\r
+    \r
+    /**\r
+     * Language versions\r
+     *\r
+     * All integral values are reserved for future version numbers.\r
+     */\r
+\r
+    /**\r
+     * The unknown version.\r
+     */\r
+    public static final int VERSION_UNKNOWN =   -1;\r
+\r
+    /**\r
+     * The default version.\r
+     */\r
+    public static final int VERSION_DEFAULT =    0;\r
+\r
+    /**\r
+     * JavaScript 1.0\r
+     */\r
+    public static final int VERSION_1_0 =      100;\r
+\r
+    /**\r
+     * JavaScript 1.1\r
+     */\r
+    public static final int VERSION_1_1 =      110;\r
+\r
+    /**\r
+     * JavaScript 1.2\r
+     */\r
+    public static final int VERSION_1_2 =      120;\r
+\r
+    /**\r
+     * JavaScript 1.3\r
+     */\r
+    public static final int VERSION_1_3 =      130;\r
+\r
+    /**\r
+     * JavaScript 1.4\r
+     */\r
+    public static final int VERSION_1_4 =      140;\r
+\r
+    /**\r
+     * JavaScript 1.5\r
+     */\r
+    public static final int VERSION_1_5 =      150;\r
+\r
+    /**\r
+     * Get the current language version.\r
+     * <p>\r
+     * The language version number affects JavaScript semantics as detailed\r
+     * in the overview documentation.\r
+     *\r
+     * @return an integer that is one of VERSION_1_0, VERSION_1_1, etc.\r
+     */\r
+    public int getLanguageVersion() {\r
+       return version;\r
+    }\r
+\r
+    /**\r
+     * Set the language version.\r
+     *\r
+     * <p>\r
+     * Setting the language version will affect functions and scripts compiled\r
+     * subsequently. See the overview documentation for version-specific\r
+     * behavior.\r
+     *\r
+     * @param version the version as specified by VERSION_1_0, VERSION_1_1, etc.\r
+     */\r
+    public void setLanguageVersion(int version) {\r
+        Object[] array = listeners;\r
+        if (array != null && version != this.version) {\r
+            firePropertyChangeImpl(array, languageVersionProperty,\r
+                               new Integer(this.version), \r
+                               new Integer(version));\r
+        }\r
+        this.version = version;\r
+    }\r
+\r
+    /**\r
+     * Get the implementation version.\r
+     *\r
+     * <p>\r
+     * The implementation version is of the form \r
+     * <pre>\r
+     *    "<i>name langVer</i> <code>release</code> <i>relNum date</i>"\r
+     * </pre>\r
+     * where <i>name</i> is the name of the product, <i>langVer</i> is \r
+     * the language version, <i>relNum</i> is the release number, and \r
+     * <i>date</i> is the release date for that specific \r
+     * release in the form "yyyy mm dd". \r
+     *\r
+     * @return a string that encodes the product, language version, release \r
+     *         number, and date.\r
+     */\r
+     public String getImplementationVersion() {\r
+        return "Rhino 1.5 release 2 2001 07 27";\r
+     }\r
+\r
+    /**\r
+     * Get the current error reporter.\r
+     *\r
+     * @see org.mozilla.javascript.ErrorReporter\r
+     */\r
+    public ErrorReporter getErrorReporter() {\r
+        if (errorReporter == null) {\r
+            errorReporter = new DefaultErrorReporter();\r
+        }\r
+        return errorReporter;\r
+    }\r
+\r
+    /**\r
+     * Change the current error reporter.\r
+     *\r
+     * @return the previous error reporter\r
+     * @see org.mozilla.javascript.ErrorReporter\r
+     */\r
+    public ErrorReporter setErrorReporter(ErrorReporter reporter) {\r
+        ErrorReporter result = errorReporter;\r
+        Object[] array = listeners;\r
+        if (array != null && errorReporter != reporter) {\r
+            firePropertyChangeImpl(array, errorReporterProperty,\r
+                                   errorReporter, reporter);\r
+        }\r
+        errorReporter = reporter;\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Get the current locale.  Returns the default locale if none has\r
+     * been set.\r
+     *\r
+     * @see java.util.Locale\r
+     */\r
+\r
+    public Locale getLocale() {\r
+        if (locale == null)\r
+            locale = Locale.getDefault();\r
+        return locale;\r
+    }\r
+\r
+    /**\r
+     * Set the current locale.\r
+     *\r
+     * @see java.util.Locale\r
+     */\r
+    public Locale setLocale(Locale loc) {\r
+        Locale result = locale;\r
+        locale = loc;\r
+        return result;\r
+    }\r
+    \r
+    /**\r
+     * Register an object to receive notifications when a bound property\r
+     * has changed\r
+     * @see java.beans.PropertyChangeEvent\r
+     * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)\r
+     * @param  listener  the listener\r
+     */\r
+    public void addPropertyChangeListener(PropertyChangeListener listener) {\r
+        synchronized (this) {\r
+            listeners = ListenerArray.add(listeners, listener);\r
+        }    \r
+    }\r
+    \r
+    /**\r
+     * Remove an object from the list of objects registered to receive \r
+     * notification of changes to a bounded property\r
+     * @see java.beans.PropertyChangeEvent\r
+     * @see #addPropertyChangeListener(java.beans.PropertyChangeListener)\r
+     * @param listener  the listener\r
+     */\r
+    public void removePropertyChangeListener(PropertyChangeListener listener) {\r
+        synchronized (this) {\r
+            listeners = ListenerArray.remove(listeners, listener);\r
+        }\r
+    }\r
+    \r
+    /**\r
+     * Notify any registered listeners that a bounded property has changed\r
+     * @see #addPropertyChangeListener(java.beans.PropertyChangeListener)\r
+     * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)\r
+     * @see java.beans.PropertyChangeListener\r
+     * @see java.beans.PropertyChangeEvent\r
+     * @param  property  the bound property\r
+     * @param  oldValue  the old value\r
+     * @param  newVale   the new value\r
+     */\r
+    void firePropertyChange(String property, Object oldValue,\r
+                            Object newValue)\r
+    {\r
+        Object[] array = listeners;\r
+        if (array != null) {\r
+            firePropertyChangeImpl(array, property, oldValue, newValue);\r
+        }\r
+    }\r
+\r
+    private void firePropertyChangeImpl(Object[] array, String property,\r
+                                        Object oldValue, Object newValue)\r
+    {\r
+        for (int i = array.length; i-- != 0;) {\r
+            Object obj = array[i];\r
+            if (obj instanceof PropertyChangeListener) {\r
+                PropertyChangeListener l = (PropertyChangeListener)obj;\r
+                l.propertyChange(new PropertyChangeEvent(\r
+                    this, property, oldValue, newValue));\r
+            }\r
+    }\r
+    }\r
+                                    \r
+    /**\r
+     * Report a warning using the error reporter for the current thread.\r
+     *\r
+     * @param message the warning message to report\r
+     * @param sourceName a string describing the source, such as a filename\r
+     * @param lineno the starting line number\r
+     * @param lineSource the text of the line (may be null)\r
+     * @param lineOffset the offset into lineSource where problem was detected\r
+     * @see org.mozilla.javascript.ErrorReporter\r
+     */\r
+    public static void reportWarning(String message, String sourceName,\r
+                                     int lineno, String lineSource,\r
+                                     int lineOffset)\r
+    {\r
+        Context cx = Context.getContext();\r
+        cx.getErrorReporter().warning(message, sourceName, lineno,\r
+                                      lineSource, lineOffset);\r
+    }\r
+\r
+    /**\r
+     * Report a warning using the error reporter for the current thread.\r
+     *\r
+     * @param message the warning message to report\r
+     * @see org.mozilla.javascript.ErrorReporter\r
+     */\r
+    public static void reportWarning(String message) {\r
+        int[] linep = { 0 };\r
+        String filename = getSourcePositionFromStack(linep);\r
+        Context.reportWarning(message, filename, linep[0], null, 0);\r
+    }\r
+\r
+    /**\r
+     * Report an error using the error reporter for the current thread.\r
+     *\r
+     * @param message the error message to report\r
+     * @param sourceName a string describing the source, such as a filename\r
+     * @param lineno the starting line number\r
+     * @param lineSource the text of the line (may be null)\r
+     * @param lineOffset the offset into lineSource where problem was detected\r
+     * @see org.mozilla.javascript.ErrorReporter\r
+     */\r
+    public static void reportError(String message, String sourceName,\r
+                                   int lineno, String lineSource,\r
+                                   int lineOffset)\r
+    {\r
+        Context cx = getCurrentContext();\r
+        if (cx != null) {\r
+            cx.errorCount++;\r
+            cx.getErrorReporter().error(message, sourceName, lineno,\r
+                                        lineSource, lineOffset);\r
+        } else {\r
+            throw new EvaluatorException(message);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Report an error using the error reporter for the current thread.\r
+     *\r
+     * @param message the error message to report\r
+     * @see org.mozilla.javascript.ErrorReporter\r
+     */\r
+    public static void reportError(String message) {\r
+        int[] linep = { 0 };\r
+        String filename = getSourcePositionFromStack(linep);\r
+        Context.reportError(message, filename, linep[0], null, 0);\r
+    }\r
+\r
+    /**\r
+     * Report a runtime error using the error reporter for the current thread.\r
+     *\r
+     * @param message the error message to report\r
+     * @param sourceName a string describing the source, such as a filename\r
+     * @param lineno the starting line number\r
+     * @param lineSource the text of the line (may be null)\r
+     * @param lineOffset the offset into lineSource where problem was detected\r
+     * @return a runtime exception that will be thrown to terminate the\r
+     *         execution of the script\r
+     * @see org.mozilla.javascript.ErrorReporter\r
+     */\r
+    public static EvaluatorException reportRuntimeError(String message,\r
+                                                      String sourceName,\r
+                                                      int lineno,\r
+                                                      String lineSource,\r
+                                                      int lineOffset)\r
+    {\r
+        Context cx = getCurrentContext();\r
+        if (cx != null) {\r
+            cx.errorCount++;\r
+            return cx.getErrorReporter().\r
+                            runtimeError(message, sourceName, lineno,\r
+                                         lineSource, lineOffset);\r
+        } else {\r
+            throw new EvaluatorException(message);\r
+        }\r
+    }\r
+\r
+    static EvaluatorException reportRuntimeError0(String messageId) {\r
+        return reportRuntimeError(getMessage0(messageId));\r
+    }\r
+\r
+    static EvaluatorException reportRuntimeError1\r
+        (String messageId, Object arg1) \r
+    {\r
+        return reportRuntimeError(getMessage1(messageId, arg1));\r
+    }\r
+\r
+    static EvaluatorException reportRuntimeError2\r
+        (String messageId, Object arg1, Object arg2) \r
+    {\r
+        return reportRuntimeError(getMessage2(messageId, arg1, arg2));\r
+    }\r
+\r
+    static EvaluatorException reportRuntimeError3\r
+        (String messageId, Object arg1, Object arg2, Object arg3) \r
+    {\r
+        return reportRuntimeError(getMessage3(messageId, arg1, arg2, arg3));\r
+    }\r
+\r
+    /**\r
+     * Report a runtime error using the error reporter for the current thread.\r
+     *\r
+     * @param message the error message to report\r
+     * @see org.mozilla.javascript.ErrorReporter\r
+     */\r
+    public static EvaluatorException reportRuntimeError(String message) {\r
+        int[] linep = { 0 };\r
+        String filename = getSourcePositionFromStack(linep);\r
+        return Context.reportRuntimeError(message, filename, linep[0], null, 0);\r
+    }\r
+\r
+    /**\r
+     * Initialize the standard objects.\r
+     *\r
+     * Creates instances of the standard objects and their constructors\r
+     * (Object, String, Number, Date, etc.), setting up 'scope' to act\r
+     * as a global object as in ECMA 15.1.<p>\r
+     *\r
+     * This method must be called to initialize a scope before scripts\r
+     * can be evaluated in that scope.\r
+     *\r
+     * @param scope the scope to initialize, or null, in which case a new\r
+     *        object will be created to serve as the scope\r
+     * @return the initialized scope\r
+     */\r
+    public Scriptable initStandardObjects(ScriptableObject scope) {\r
+        return initStandardObjects(scope, false);\r
+    }\r
+    \r
+    /**\r
+     * Initialize the standard objects.\r
+     *\r
+     * Creates instances of the standard objects and their constructors\r
+     * (Object, String, Number, Date, etc.), setting up 'scope' to act\r
+     * as a global object as in ECMA 15.1.<p>\r
+     *\r
+     * This method must be called to initialize a scope before scripts\r
+     * can be evaluated in that scope.<p>\r
+     * \r
+     * This form of the method also allows for creating "sealed" standard\r
+     * objects. An object that is sealed cannot have properties added or\r
+     * removed. This is useful to create a "superglobal" that can be shared \r
+     * among several top-level objects. Note that sealing is not allowed in\r
+     * the current ECMA/ISO language specification, but is likely for\r
+     * the next version.\r
+     *\r
+     * @param scope the scope to initialize, or null, in which case a new\r
+     *        object will be created to serve as the scope\r
+     * @param sealed whether or not to create sealed standard objects that\r
+     *        cannot be modified. \r
+     * @return the initialized scope\r
+     * @since 1.4R3\r
+     */\r
+    public ScriptableObject initStandardObjects(ScriptableObject scope,\r
+                                                boolean sealed)\r
+    {\r
+        if (scope == null)\r
+            scope = new NativeObject();\r
+\r
+        BaseFunction.init(this, scope, sealed);\r
+        NativeObject.init(this, scope, sealed);\r
+\r
+        Scriptable objectProto = ScriptableObject.getObjectPrototype(scope);\r
+\r
+        // Function.prototype.__proto__ should be Object.prototype\r
+        Scriptable functionProto = ScriptableObject.getFunctionPrototype(scope);\r
+        functionProto.setPrototype(objectProto);\r
+\r
+        // Set the prototype of the object passed in if need be\r
+        if (scope.getPrototype() == null)\r
+            scope.setPrototype(objectProto);\r
+\r
+        // must precede NativeGlobal since it's needed therein\r
+        NativeError.init(this, scope, sealed);\r
+        NativeGlobal.init(this, scope, sealed);\r
+\r
+        NativeArray.init(this, scope, sealed);\r
+        NativeString.init(this, scope, sealed);\r
+        NativeBoolean.init(this, scope, sealed);\r
+        NativeNumber.init(this, scope, sealed);\r
+        NativeDate.init(this, scope, sealed);\r
+        NativeMath.init(this, scope, sealed);\r
+\r
+        NativeWith.init(this, scope, sealed);\r
+        NativeCall.init(this, scope, sealed);\r
+        NativeScript.init(this, scope, sealed);\r
+\r
+        new LazilyLoadedCtor(scope, \r
+                             "RegExp",\r
+                             "org.mozilla.javascript.regexp.NativeRegExp",\r
+                             sealed);\r
+\r
+        // This creates the Packages and java package roots.\r
+        new LazilyLoadedCtor(scope, \r
+                             "Packages",\r
+                             "org.mozilla.javascript.NativeJavaPackage",\r
+                             sealed);\r
+        new LazilyLoadedCtor(scope, \r
+                             "java", \r
+                             "org.mozilla.javascript.NativeJavaPackage",\r
+                             sealed);\r
+        new LazilyLoadedCtor(scope, \r
+                             "getClass",\r
+                             "org.mozilla.javascript.NativeJavaPackage",\r
+                             sealed);\r
\r
+        // Define the JavaAdapter class, allowing it to be overridden.\r
+        String adapterClass = "org.mozilla.javascript.JavaAdapter";\r
+        String adapterProperty = "JavaAdapter";\r
+        try {\r
+            adapterClass = System.getProperty(adapterClass, adapterClass);\r
+            adapterProperty = System.getProperty\r
+                ("org.mozilla.javascript.JavaAdapterClassName",\r
+                 adapterProperty);\r
+        }\r
+        catch (SecurityException e) {\r
+            // We may not be allowed to get system properties. Just\r
+            // use the default adapter in that case.\r
+        }\r
+\r
+        new LazilyLoadedCtor(scope, adapterProperty, adapterClass, sealed);\r
+\r
+        return scope;\r
+    }\r
+    \r
+    /**\r
+     * Get the singleton object that represents the JavaScript Undefined value.\r
+     */\r
+    public static Object getUndefinedValue() {\r
+        return Undefined.instance;\r
+    }\r
+    \r
+    /**\r
+     * Evaluate a JavaScript source string.\r
+     *\r
+     * The provided source name and line number are used for error messages\r
+     * and for producing debug information.\r
+     *\r
+     * @param scope the scope to execute in\r
+     * @param source the JavaScript source\r
+     * @param sourceName a string describing the source, such as a filename\r
+     * @param lineno the starting line number\r
+     * @param securityDomain an arbitrary object that specifies security \r
+     *        information about the origin or owner of the script. For \r
+     *        implementations that don't care about security, this value \r
+     *        may be null.\r
+     * @return the result of evaluating the string\r
+     * @exception JavaScriptException if an uncaught JavaScript exception\r
+     *            occurred while evaluating the source string\r
+     * @see org.mozilla.javascript.SecuritySupport\r
+     */\r
+    public Object evaluateString(Scriptable scope, String source,\r
+                                 String sourceName, int lineno,\r
+                                 Object securityDomain)\r
+        throws JavaScriptException\r
+    {\r
+        try {\r
+            Reader in = new StringReader(source);\r
+            return evaluateReader(scope, in, sourceName, lineno, \r
+                                  securityDomain);\r
+        }\r
+        catch (IOException ioe) {\r
+            // Should never occur because we just made the reader from a String\r
+            throw new RuntimeException();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Evaluate a reader as JavaScript source.\r
+     *\r
+     * All characters of the reader are consumed.\r
+     *\r
+     * @param scope the scope to execute in\r
+     * @param in the Reader to get JavaScript source from\r
+     * @param sourceName a string describing the source, such as a filename\r
+     * @param lineno the starting line number\r
+     * @param securityDomain an arbitrary object that specifies security \r
+     *        information about the origin or owner of the script. For \r
+     *        implementations that don't care about security, this value \r
+     *        may be null.\r
+     * @return the result of evaluating the source\r
+     *\r
+     * @exception IOException if an IOException was generated by the Reader\r
+     * @exception JavaScriptException if an uncaught JavaScript exception\r
+     *            occurred while evaluating the Reader\r
+     */\r
+    public Object evaluateReader(Scriptable scope, Reader in,\r
+                                 String sourceName, int lineno,\r
+                                 Object securityDomain)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Script script = compileReader(scope, in, sourceName, lineno, \r
+                                      securityDomain);\r
+        if (script != null)\r
+            return script.exec(this, scope);\r
+        else\r
+            return null;\r
+    }\r
+\r
+    /**\r
+     * Check whether a string is ready to be compiled.\r
+     * <p>\r
+     * stringIsCompilableUnit is intended to support interactive compilation of\r
+     * javascript.  If compiling the string would result in an error\r
+     * that might be fixed by appending more source, this method\r
+     * returns false.  In every other case, it returns true.\r
+     * <p>\r
+     * Interactive shells may accumulate source lines, using this\r
+     * method after each new line is appended to check whether the\r
+     * statement being entered is complete.\r
+     *\r
+     * @param source the source buffer to check\r
+     * @return whether the source is ready for compilation\r
+     * @since 1.4 Release 2\r
+     */\r
+    synchronized public boolean stringIsCompilableUnit(String source)\r
+    {\r
+        Reader in = new StringReader(source);\r
+        // no source name or source text manager, because we're just\r
+        // going to throw away the result.\r
+        TokenStream ts = new TokenStream(in, null, null, 1);\r
+\r
+        // Temporarily set error reporter to always be the exception-throwing\r
+        // DefaultErrorReporter.  (This is why the method is synchronized...)\r
+        ErrorReporter currentReporter = \r
+            setErrorReporter(new DefaultErrorReporter());\r
+\r
+        boolean errorseen = false;\r
+        try {\r
+            IRFactory irf = new IRFactory(ts, null);\r
+            Parser p = new Parser(irf);\r
+            p.parse(ts);\r
+        } catch (IOException ioe) {\r
+            errorseen = true;\r
+        } catch (EvaluatorException ee) {\r
+            errorseen = true;\r
+        } finally {\r
+            // Restore the old error reporter.\r
+            setErrorReporter(currentReporter);\r
+        }\r
+        // Return false only if an error occurred as a result of reading past\r
+        // the end of the file, i.e. if the source could be fixed by\r
+        // appending more source.\r
+        if (errorseen && ts.eof())\r
+            return false;\r
+        else \r
+            return true;\r
+    }\r
+\r
+    /**\r
+     * Compiles the source in the given reader.\r
+     * <p>\r
+     * Returns a script that may later be executed.\r
+     * Will consume all the source in the reader.\r
+     *\r
+     * @param scope if nonnull, will be the scope in which the script object\r
+     *        is created. The script object will be a valid JavaScript object\r
+     *        as if it were created using the JavaScript1.3 Script constructor\r
+     * @param in the input reader\r
+     * @param sourceName a string describing the source, such as a filename\r
+     * @param lineno the starting line number for reporting errors\r
+     * @param securityDomain an arbitrary object that specifies security \r
+     *        information about the origin or owner of the script. For \r
+     *        implementations that don't care about security, this value \r
+     *        may be null.\r
+     * @return a script that may later be executed\r
+     * @see org.mozilla.javascript.Script#exec\r
+     * @exception IOException if an IOException was generated by the Reader\r
+     */\r
+    public Script compileReader(Scriptable scope, Reader in, String sourceName,\r
+                                int lineno, Object securityDomain)\r
+        throws IOException\r
+    {\r
+        return (Script) compile(scope, in, sourceName, lineno, securityDomain, \r
+                                false);\r
+    }\r
+\r
+\r
+    /**\r
+     * Compile a JavaScript function.\r
+     * <p>\r
+     * The function source must be a function definition as defined by\r
+     * ECMA (e.g., "function f(a) { return a; }"). \r
+     *\r
+     * @param scope the scope to compile relative to\r
+     * @param source the function definition source\r
+     * @param sourceName a string describing the source, such as a filename\r
+     * @param lineno the starting line number\r
+     * @param securityDomain an arbitrary object that specifies security \r
+     *        information about the origin or owner of the script. For \r
+     *        implementations that don't care about security, this value \r
+     *        may be null.\r
+     * @return a Function that may later be called\r
+     * @see org.mozilla.javascript.Function\r
+     */\r
+    public Function compileFunction(Scriptable scope, String source,\r
+                                    String sourceName, int lineno,\r
+                                    Object securityDomain)\r
+    {\r
+        Reader in = new StringReader(source);\r
+        try {\r
+            return (Function) compile(scope, in, sourceName, lineno, \r
+                                      securityDomain, true);\r
+        }\r
+        catch (IOException ioe) {\r
+            // Should never happen because we just made the reader\r
+            // from a String\r
+            throw new RuntimeException();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Decompile the script.\r
+     * <p>\r
+     * The canonical source of the script is returned.\r
+     *\r
+     * @param script the script to decompile\r
+     * @param scope the scope under which to decompile\r
+     * @param indent the number of spaces to indent the result\r
+     * @return a string representing the script source\r
+     */\r
+     public String decompileScript(Script script, Scriptable scope,\r
+                                   int indent)\r
+    {\r
+        NativeScript ns = (NativeScript) script;\r
+        ns.initScript(scope);\r
+        return ns.decompile(this, indent, false);\r
+    }\r
+\r
+    /**\r
+     * Decompile a JavaScript Function.\r
+     * <p>\r
+     * Decompiles a previously compiled JavaScript function object to\r
+     * canonical source.\r
+     * <p>\r
+     * Returns function body of '[native code]' if no decompilation\r
+     * information is available.\r
+     *\r
+     * @param fun the JavaScript function to decompile\r
+     * @param indent the number of spaces to indent the result\r
+     * @return a string representing the function source\r
+     */\r
+    public String decompileFunction(Function fun, int indent) {\r
+        if (fun instanceof BaseFunction)\r
+            return ((BaseFunction)fun).decompile(this, indent, false);\r
+        else\r
+            return "function " + fun.getClassName() +\r
+                   "() {\n\t[native code]\n}\n";\r
+    }\r
+\r
+    /**\r
+     * Decompile the body of a JavaScript Function.\r
+     * <p>\r
+     * Decompiles the body a previously compiled JavaScript Function\r
+     * object to canonical source, omitting the function header and\r
+     * trailing brace.\r
+     *\r
+     * Returns '[native code]' if no decompilation information is available.\r
+     *\r
+     * @param fun the JavaScript function to decompile\r
+     * @param indent the number of spaces to indent the result\r
+     * @return a string representing the function body source.\r
+     */\r
+    public String decompileFunctionBody(Function fun, int indent) {\r
+        if (fun instanceof BaseFunction)\r
+            return ((BaseFunction)fun).decompile(this, indent, true);\r
+        else\r
+            // not sure what the right response here is.  JSRef currently\r
+            // dumps core.\r
+            return "[native code]\n";\r
+    }\r
+\r
+    /**\r
+     * Create a new JavaScript object.\r
+     *\r
+     * Equivalent to evaluating "new Object()".\r
+     * @param scope the scope to search for the constructor and to evaluate\r
+     *              against\r
+     * @return the new object\r
+     * @exception PropertyException if "Object" cannot be found in\r
+     *            the scope\r
+     * @exception NotAFunctionException if the "Object" found in the scope\r
+     *            is not a function\r
+     * @exception JavaScriptException if an uncaught JavaScript exception\r
+     *            occurred while creating the object\r
+     */\r
+    public Scriptable newObject(Scriptable scope)\r
+        throws PropertyException,\r
+               NotAFunctionException,\r
+               JavaScriptException\r
+    {\r
+        return newObject(scope, "Object", null);\r
+    }\r
+\r
+    /**\r
+     * Create a new JavaScript object by executing the named constructor.\r
+     *\r
+     * The call <code>newObject(scope, "Foo")</code> is equivalent to\r
+     * evaluating "new Foo()".\r
+     *\r
+     * @param scope the scope to search for the constructor and to evaluate against\r
+     * @param constructorName the name of the constructor to call\r
+     * @return the new object\r
+     * @exception PropertyException if a property with the constructor\r
+     *            name cannot be found in the scope\r
+     * @exception NotAFunctionException if the property found in the scope\r
+     *            is not a function\r
+     * @exception JavaScriptException if an uncaught JavaScript exception\r
+     *            occurred while creating the object\r
+     */\r
+    public Scriptable newObject(Scriptable scope, String constructorName)\r
+        throws PropertyException,\r
+               NotAFunctionException,\r
+               JavaScriptException\r
+    {\r
+        return newObject(scope, constructorName, null);\r
+    }\r
+\r
+    /**\r
+     * Creates a new JavaScript object by executing the named constructor.\r
+     *\r
+     * Searches <code>scope</code> for the named constructor, calls it with\r
+     * the given arguments, and returns the result.<p>\r
+     *\r
+     * The code\r
+     * <pre>\r
+     * Object[] args = { "a", "b" };\r
+     * newObject(scope, "Foo", args)</pre>\r
+     * is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo\r
+     * constructor has been defined in <code>scope</code>.\r
+     *\r
+     * @param scope The scope to search for the constructor and to evaluate\r
+     *              against\r
+     * @param constructorName the name of the constructor to call\r
+     * @param args the array of arguments for the constructor\r
+     * @return the new object\r
+     * @exception PropertyException if a property with the constructor\r
+     *            name cannot be found in the scope\r
+     * @exception NotAFunctionException if the property found in the scope\r
+     *            is not a function\r
+     * @exception JavaScriptException if an uncaught JavaScript exception\r
+     *            occurs while creating the object\r
+     */\r
+    public Scriptable newObject(Scriptable scope, String constructorName,\r
+                                Object[] args)\r
+        throws PropertyException,\r
+               NotAFunctionException,\r
+               JavaScriptException\r
+    {\r
+        Object ctorVal = ScriptRuntime.getTopLevelProp(scope, constructorName);\r
+        if (ctorVal == Scriptable.NOT_FOUND) {\r
+            String message = getMessage1("msg.ctor.not.found", constructorName);\r
+            throw new PropertyException(message);\r
+        }\r
+        if (!(ctorVal instanceof Function)) {\r
+            String message = getMessage1("msg.not.ctor", constructorName);\r
+            throw new NotAFunctionException(message);\r
+        }\r
+        Function ctor = (Function) ctorVal;\r
+        return ctor.construct(this, ctor.getParentScope(),\r
+                              (args == null) ? ScriptRuntime.emptyArgs : args);\r
+    }\r
+\r
+    /**\r
+     * Create an array with a specified initial length.\r
+     * <p>\r
+     * @param scope the scope to create the object in\r
+     * @param length the initial length (JavaScript arrays may have\r
+     *               additional properties added dynamically).\r
+     * @return the new array object\r
+     */\r
+    public Scriptable newArray(Scriptable scope, int length) {\r
+        Scriptable result = new NativeArray(length);\r
+        newArrayHelper(scope, result);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Create an array with a set of initial elements.\r
+     * <p>\r
+     * @param scope the scope to create the object in\r
+     * @param elements the initial elements. Each object in this array\r
+     *                 must be an acceptable JavaScript type.\r
+     * @return the new array object\r
+     */\r
+    public Scriptable newArray(Scriptable scope, Object[] elements) {\r
+        Scriptable result = new NativeArray(elements);\r
+        newArrayHelper(scope, result);\r
+        return result;\r
+    }\r
+    \r
+    /**\r
+     * Get the elements of a JavaScript array.\r
+     * <p>\r
+     * If the object defines a length property, a Java array with that\r
+     * length is created and initialized with the values obtained by\r
+     * calling get() on object for each value of i in [0,length-1]. If\r
+     * there is not a defined value for a property the Undefined value\r
+     * is used to initialize the corresponding element in the array. The\r
+     * Java array is then returned.\r
+     * If the object doesn't define a length property, null is returned.\r
+     * @param object the JavaScript array or array-like object\r
+     * @return a Java array of objects\r
+     * @since 1.4 release 2\r
+     */\r
+    public Object[] getElements(Scriptable object) {\r
+        double doubleLen = NativeArray.getLengthProperty(object);\r
+        if (doubleLen != doubleLen)\r
+            return null;\r
+        int len = (int) doubleLen;\r
+        Object[] result = new Object[len];\r
+        for (int i=0; i < len; i++) {\r
+            Object elem = object.get(i, object);\r
+            result[i] = elem == Scriptable.NOT_FOUND ? Undefined.instance \r
+                                                     : elem;\r
+        }\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Convert the value to a JavaScript boolean value.\r
+     * <p>\r
+     * See ECMA 9.2.\r
+     *\r
+     * @param value a JavaScript value\r
+     * @return the corresponding boolean value converted using\r
+     *         the ECMA rules\r
+     */\r
+    public static boolean toBoolean(Object value) {\r
+        return ScriptRuntime.toBoolean(value);\r
+    }\r
+\r
+    /**\r
+     * Convert the value to a JavaScript Number value.\r
+     * <p>\r
+     * Returns a Java double for the JavaScript Number.\r
+     * <p>\r
+     * See ECMA 9.3.\r
+     *\r
+     * @param value a JavaScript value\r
+     * @return the corresponding double value converted using\r
+     *         the ECMA rules\r
+     */\r
+    public static double toNumber(Object value) {\r
+        return ScriptRuntime.toNumber(value);\r
+    }\r
+\r
+    /**\r
+     * Convert the value to a JavaScript String value.\r
+     * <p>\r
+     * See ECMA 9.8.\r
+     * <p>\r
+     * @param value a JavaScript value\r
+     * @return the corresponding String value converted using\r
+     *         the ECMA rules\r
+     */\r
+    public static String toString(Object value) {\r
+        return ScriptRuntime.toString(value);\r
+    }\r
+\r
+    /**\r
+     * Convert the value to an JavaScript object value.\r
+     * <p>\r
+     * Note that a scope must be provided to look up the constructors\r
+     * for Number, Boolean, and String.\r
+     * <p>\r
+     * See ECMA 9.9.\r
+     * <p>\r
+     * Additionally, arbitrary Java objects and classes will be\r
+     * wrapped in a Scriptable object with its Java fields and methods\r
+     * reflected as JavaScript properties of the object.\r
+     *\r
+     * @param value any Java object\r
+     * @param scope global scope containing constructors for Number,\r
+     *              Boolean, and String\r
+     * @return new JavaScript object\r
+     */\r
+    public static Scriptable toObject(Object value, Scriptable scope) {\r
+        return ScriptRuntime.toObject(scope, value, null);\r
+    }\r
+    \r
+    /**\r
+     * Convert the value to an JavaScript object value.\r
+     * <p>\r
+     * Note that a scope must be provided to look up the constructors\r
+     * for Number, Boolean, and String.\r
+     * <p>\r
+     * See ECMA 9.9.\r
+     * <p>\r
+     * Additionally, arbitrary Java objects and classes will be\r
+     * wrapped in a Scriptable object with its Java fields and methods\r
+     * reflected as JavaScript properties of the object. If the \r
+     * "staticType" parameter is provided, it will be used as the static\r
+     * type of the Java value to create.\r
+     *\r
+     * @param value any Java object\r
+     * @param scope global scope containing constructors for Number,\r
+     *              Boolean, and String\r
+     * @param staticType the static type of the Java value to create\r
+     * @return new JavaScript object\r
+     */\r
+    public static Scriptable toObject(Object value, Scriptable scope, \r
+                                      Class staticType) {\r
+        if (value == null && staticType != null)\r
+            return null;\r
+        return ScriptRuntime.toObject(scope, value, staticType);\r
+    }\r
+\r
+    /**\r
+     * Tell whether debug information is being generated.\r
+     * @since 1.3\r
+     */\r
+    public boolean isGeneratingDebug() {\r
+        return generatingDebug;\r
+    }\r
+\r
+    /**\r
+     * Specify whether or not debug information should be generated.\r
+     * <p>\r
+     * Setting the generation of debug information on will set the\r
+     * optimization level to zero.\r
+     * @since 1.3\r
+     */\r
+    public void setGeneratingDebug(boolean generatingDebug) {\r
+        generatingDebugChanged = true;\r
+        if (generatingDebug)\r
+            setOptimizationLevel(0);\r
+        this.generatingDebug = generatingDebug;\r
+    }\r
+\r
+    /**\r
+     * Tell whether source information is being generated.\r
+     * @since 1.3\r
+     */\r
+    public boolean isGeneratingSource() {\r
+        return generatingSource;\r
+    }\r
+\r
+    /**\r
+     * Specify whether or not source information should be generated.\r
+     * <p>\r
+     * Without source information, evaluating the "toString" method\r
+     * on JavaScript functions produces only "[native code]" for\r
+     * the body of the function.\r
+     * Note that code generated without source is not fully ECMA\r
+     * conformant.\r
+     * @since 1.3\r
+     */\r
+    public void setGeneratingSource(boolean generatingSource) {\r
+        this.generatingSource = generatingSource;\r
+    }\r
+\r
+    /**\r
+     * Get the current optimization level.\r
+     * <p>\r
+     * The optimization level is expressed as an integer between -1 and\r
+     * 9.\r
+     * @since 1.3\r
+     *\r
+     */\r
+    public int getOptimizationLevel() {\r
+        return optimizationLevel;\r
+    }\r
+\r
+    /**\r
+     * Set the current optimization level.\r
+     * <p>\r
+     * The optimization level is expected to be an integer between -1 and\r
+     * 9. Any negative values will be interpreted as -1, and any values\r
+     * greater than 9 will be interpreted as 9.\r
+     * An optimization level of -1 indicates that interpretive mode will\r
+     * always be used. Levels 0 through 9 indicate that class files may \r
+     * be generated. Higher optimization levels trade off compile time\r
+     * performance for runtime performance.\r
+     * The optimizer level can't be set greater than -1 if the optimizer\r
+     * package doesn't exist at run time.\r
+     * @param optimizationLevel an integer indicating the level of\r
+     *        optimization to perform\r
+     * @since 1.3\r
+     *\r
+     */\r
+    public void setOptimizationLevel(int optimizationLevel) {\r
+        if (optimizationLevel < 0) {\r
+            optimizationLevel = -1;\r
+        } else if (optimizationLevel > 9) {\r
+                optimizationLevel = 9;\r
+        }\r
+        if (codegenClass == null)\r
+            optimizationLevel = -1;\r
+        this.optimizationLevel = optimizationLevel;\r
+    }\r
+\r
+    /**\r
+     * Get the current target class file name.\r
+     * <p>\r
+     * If nonnull, requests to compile source will result in one or\r
+     * more class files being generated.\r
+     * @since 1.3\r
+     */\r
+    public String getTargetClassFileName() {\r
+        return nameHelper == null\r
+               ? null \r
+               : nameHelper.getTargetClassFileName();\r
+    }\r
+\r
+    /**\r
+     * Set the current target class file name.\r
+     * <p>\r
+     * If nonnull, requests to compile source will result in one or\r
+     * more class files being generated. If null, classes will only\r
+     * be generated in memory.\r
+     *\r
+     * @since 1.3\r
+     */\r
+    public void setTargetClassFileName(String classFileName) {\r
+        if (nameHelper != null)\r
+            nameHelper.setTargetClassFileName(classFileName);\r
+    }\r
+\r
+    /**\r
+     * Get the current package to generate classes into.\r
+     *\r
+     * @since 1.3\r
+     */\r
+    public String getTargetPackage() {\r
+        return (nameHelper == null) ? null : nameHelper.getTargetPackage();\r
+    }\r
+\r
+    /**\r
+     * Set the package to generate classes into.\r
+     *\r
+     * @since 1.3\r
+     */\r
+    public void setTargetPackage(String targetPackage) {\r
+        if (nameHelper != null)\r
+            nameHelper.setTargetPackage(targetPackage);\r
+    }\r
+\r
+    /**\r
+     * Get the current interface to write class bytes into.\r
+     *\r
+     * @see ClassOutput\r
+     * @since 1.5 Release 2\r
+     */\r
+    public ClassOutput getClassOutput() {\r
+        return nameHelper == null ? null : nameHelper.getClassOutput();\r
+    }\r
+\r
+    /**\r
+     * Set the interface to write class bytes into.\r
+     * Unless setTargetClassFileName() has been called classOutput will be\r
+     * used each time the javascript compiler has generated the bytecode for a\r
+     * script class.\r
+     *\r
+     * @see ClassOutput\r
+     * @since 1.5 Release 2\r
+     */\r
+    public void setClassOutput(ClassOutput classOutput) {\r
+        if (nameHelper != null)\r
+            nameHelper.setClassOutput(classOutput);\r
+    }\r
+    \r
+    /**\r
+     * Add a Context listener.\r
+     */\r
+    public static void addContextListener(ContextListener listener) {\r
+        synchronized (staticDataLock) {\r
+            contextListeners = ListenerArray.add(contextListeners, listener);\r
+        }\r
+    }\r
+    \r
+    /**\r
+     * Remove a Context listener.\r
+     * @param listener the listener to remove.\r
+     */\r
+    public static void removeContextListener(ContextListener listener) {\r
+        synchronized (staticDataLock) {\r
+            contextListeners = ListenerArray.remove(contextListeners, listener);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Set the security support for this context. \r
+     * <p> SecuritySupport may only be set if it is currently null.\r
+     * Otherwise a SecurityException is thrown.\r
+     * @param supportObj a SecuritySupport object\r
+     * @throws SecurityException if there is already a SecuritySupport\r
+     *         object for this Context\r
+     */\r
+    public synchronized void setSecuritySupport(SecuritySupport supportObj) {\r
+        if (securitySupport != null) {\r
+            throw new SecurityException("Cannot overwrite existing " +\r
+                                        "SecuritySupport object");\r
+        }\r
+        securitySupport = supportObj;\r
+    }\r
+        \r
+    /**\r
+     * Return true if a security domain is required on calls to\r
+     * compile and evaluate scripts.\r
+     *\r
+     * @since 1.4 Release 2\r
+     */\r
+    public static boolean isSecurityDomainRequired() { \r
+        return requireSecurityDomain;\r
+    }\r
+    \r
+    /**\r
+     * Returns the security context associated with the innermost\r
+     * script or function being executed by the interpreter.\r
+     * @since 1.4 release 2\r
+     */\r
+    public Object getInterpreterSecurityDomain() {\r
+        return interpreterSecurityDomain;\r
+    }\r
+    \r
+    /**\r
+     * Returns true if the class parameter is a class in the \r
+     * interpreter. Typically used by embeddings that get a class\r
+     * context to check security. These embeddings must know \r
+     * whether to get the security context associated with the\r
+     * interpreter or not.\r
+     * \r
+     * @param cl a class to test whether or not it is an interpreter\r
+     *           class\r
+     * @return true if cl is an interpreter class\r
+     * @since 1.4 release 2\r
+     */\r
+    public boolean isInterpreterClass(Class cl) {\r
+        return cl == Interpreter.class;\r
+    }\r
+    \r
+    /**\r
+     * Set the class that the generated target will extend.\r
+     * \r
+     * @param extendsClass the class it extends\r
+     */\r
+    public void setTargetExtends(Class extendsClass) {\r
+        if (nameHelper != null) {\r
+            nameHelper.setTargetExtends(extendsClass);\r
+        }\r
+    }\r
+       \r
+    /**\r
+     * Set the interfaces that the generated target will implement.\r
+     * \r
+     * @param implementsClasses an array of Class objects, one for each\r
+     *                          interface the target will extend\r
+     */\r
+    public void setTargetImplements(Class[] implementsClasses) {\r
+        if (nameHelper != null) {\r
+            nameHelper.setTargetImplements(implementsClasses);\r
+        }\r
+    }\r
+        \r
+    /**\r
+     * Get a value corresponding to a key.\r
+     * <p>\r
+     * Since the Context is associated with a thread it can be \r
+     * used to maintain values that can be later retrieved using \r
+     * the current thread. \r
+     * <p>\r
+     * Note that the values are maintained with the Context, so\r
+     * if the Context is disassociated from the thread the values\r
+     * cannot be retreived. Also, if private data is to be maintained\r
+     * in this manner the key should be a java.lang.Object \r
+     * whose reference is not divulged to untrusted code.\r
+     * @param key the key used to lookup the value\r
+     * @return a value previously stored using putThreadLocal.\r
+     */\r
+    public Object getThreadLocal(Object key) {\r
+        if (hashtable == null)\r
+            return null;\r
+        return hashtable.get(key);\r
+    }\r
+\r
+    /**\r
+     * Put a value that can later be retrieved using a given key.\r
+     * <p>\r
+     * @param key the key used to index the value\r
+     * @param value the value to save\r
+     */\r
+    public void putThreadLocal(Object key, Object value) {\r
+        if (hashtable == null)\r
+            hashtable = new Hashtable();\r
+        hashtable.put(key, value);\r
+    }\r
+    \r
+    /**\r
+     * Remove values from thread-local storage.\r
+     * @param key the key for the entry to remove.\r
+     * @since 1.5 release 2\r
+     */\r
+    public void removeThreadLocal(Object key) {\r
+        if (hashtable == null)\r
+            return;\r
+        hashtable.remove(key);\r
+    }    \r
+    \r
+    /**\r
+     * Return whether functions are compiled by this context using\r
+     * dynamic scope.\r
+     * <p>\r
+     * If functions are compiled with dynamic scope, then they execute\r
+     * in the scope of their caller, rather than in their parent scope.\r
+     * This is useful for sharing functions across multiple scopes.\r
+     * @since 1.5 Release 1\r
+     */\r
+    public boolean hasCompileFunctionsWithDynamicScope() {\r
+        return compileFunctionsWithDynamicScopeFlag;\r
+    }\r
+    \r
+    /**\r
+     * Set whether functions compiled by this context should use\r
+     * dynamic scope.\r
+     * <p>\r
+     * @param flag if true, compile functions with dynamic scope\r
+     * @since 1.5 Release 1\r
+     */\r
+    public void setCompileFunctionsWithDynamicScope(boolean flag) {\r
+        compileFunctionsWithDynamicScopeFlag = flag;\r
+    }\r
+    \r
+    /**\r
+     * Set whether to cache some values statically.\r
+     * <p>\r
+     * By default, the engine will cache some values statically \r
+     * (reflected Java classes, for instance). This can speed\r
+     * execution dramatically, but increases the memory footprint.\r
+     * Also, with caching enabled, references may be held to \r
+     * objects past the lifetime of any real usage. \r
+     * <p>\r
+     * If caching is enabled and this method is called with a \r
+     * <code>false</code> argument, the caches will be emptied.\r
+     * So one strategy could be to clear the caches at times\r
+     * appropriate to the application.\r
+     * <p>\r
+     * Caching is enabled by default.\r
+     * \r
+     * @param cachingEnabled if true, caching is enabled\r
+     * @since 1.5 Release 1 \r
+     */\r
+    public static void setCachingEnabled(boolean cachingEnabled) {\r
+        if (isCachingEnabled && !cachingEnabled) {\r
+            // Caching is being turned off. Empty caches.\r
+            JavaMembers.classTable = new Hashtable();\r
+            nameHelper.reset();\r
+        }\r
+        isCachingEnabled = cachingEnabled;\r
+        FunctionObject.setCachingEnabled(cachingEnabled);\r
+    }\r
+    \r
+    /**\r
+     * Set a WrapHandler for this Context.\r
+     * <p>\r
+     * The WrapHandler allows custom object wrapping behavior for \r
+     * Java object manipulated with JavaScript.\r
+     * @see org.mozilla.javascript.WrapHandler\r
+     * @since 1.5 Release 2 \r
+     */\r
+    public void setWrapHandler(WrapHandler wrapHandler) {\r
+        this.wrapHandler = wrapHandler;\r
+    }\r
+    \r
+    /**\r
+     * Return the current WrapHandler, or null if none is defined.\r
+     * @see org.mozilla.javascript.WrapHandler\r
+     * @since 1.5 Release 2 \r
+     */\r
+    public WrapHandler getWrapHandler() {\r
+        return wrapHandler;\r
+    }\r
+    \r
+    public DebuggableEngine getDebuggableEngine() {\r
+        if (debuggableEngine == null)\r
+            debuggableEngine = new DebuggableEngineImpl(this);\r
+        return debuggableEngine;\r
+    }\r
+    \r
+    \r
+    /**\r
+     * if hasFeature(FEATURE_NON_ECMA_GET_YEAR) returns true,\r
+     * Date.prototype.getYear subtructs 1900 only if 1900 <= date < 2000\r
+     * in deviation with Ecma B.2.4\r
+     */\r
+    public static final int FEATURE_NON_ECMA_GET_YEAR = 1;\r
+    \r
+    /**\r
+     * Controls certain aspects of script semantics. \r
+     * Should be overwritten to alter default behavior.\r
+     * @param featureIndex feature index to check\r
+     * @return true if the <code>featureIndex</code> feature is turned on\r
+     * @see #FEATURE_NON_ECMA_GET_YEAR\r
+     */\r
+    public boolean hasFeature(int featureIndex) {\r
+        if (featureIndex == FEATURE_NON_ECMA_GET_YEAR) {\r
+           /*\r
+            * During the great date rewrite of 1.3, we tried to track the\r
+            * evolving ECMA standard, which then had a definition of\r
+            * getYear which always subtracted 1900.  Which we\r
+            * implemented, not realizing that it was incompatible with\r
+            * the old behavior...  now, rather than thrash the behavior\r
+            * yet again, we've decided to leave it with the - 1900\r
+            * behavior and point people to the getFullYear method.  But\r
+            * we try to protect existing scripts that have specified a\r
+            * version...\r
+            */\r
+            return (version == Context.VERSION_1_0 \r
+                    || version == Context.VERSION_1_1\r
+                    || version == Context.VERSION_1_2);\r
+        }\r
+        throw new RuntimeException("Bad feature index: " + featureIndex);\r
+    }\r
+\r
+    /**\r
+     * Get/Set threshold of executed instructions counter that triggers call to\r
+     * <code>observeInstructionCount()</code>.\r
+     * When the threshold is zero, instruction counting is disabled, \r
+     * otherwise each time the run-time executes at least the threshold value\r
+     * of script instructions, <code>observeInstructionCount()</code> will \r
+     * be called.\r
+     */\r
+    public int getInstructionObserverThreshold() {\r
+        return instructionThreshold;\r
+    }\r
+    \r
+    public void setInstructionObserverThreshold(int threshold) {\r
+        instructionThreshold = threshold;\r
+    }\r
+    \r
+    /** \r
+     * Allow application to monitor counter of executed script instructions\r
+     * in Context subclasses.\r
+     * Run-time calls this when instruction counting is enabled and the counter\r
+     * reaches limit set by <code>setInstructionObserverThreshold()</code>.\r
+     * The method is useful to observe long running scripts and if necessary\r
+     * to terminate them.\r
+     * @param instructionCount amount of script instruction executed since \r
+     * last call to <code>observeInstructionCount</code> \r
+     * @throws Error to terminate the script\r
+     */\r
+    protected void observeInstructionCount(int instructionCount) {}\r
+    \r
+    /********** end of API **********/\r
+    \r
+    void pushFrame(DebugFrame frame) {\r
+        if (frameStack == null)\r
+            frameStack = new java.util.Stack();\r
+        frameStack.push(frame);\r
+    }\r
+    \r
+    void popFrame() {\r
+        frameStack.pop();\r
+    }\r
+    \r
+\r
+\r
+    static String getMessage0(String messageId) {\r
+        return getMessage(messageId, null);\r
+    }\r
+\r
+    static String getMessage1(String messageId, Object arg1) {\r
+        Object[] arguments = {arg1};\r
+        return getMessage(messageId, arguments);\r
+    }\r
+\r
+    static String getMessage2(String messageId, Object arg1, Object arg2) {\r
+        Object[] arguments = {arg1, arg2};\r
+        return getMessage(messageId, arguments);\r
+    }\r
+\r
+    static String getMessage3\r
+        (String messageId, Object arg1, Object arg2, Object arg3) {\r
+        Object[] arguments = {arg1, arg2, arg3};\r
+        return getMessage(messageId, arguments);\r
+    }\r
+    /**\r
+     * Internal method that reports an error for missing calls to\r
+     * enter().\r
+     */\r
+    static Context getContext() {\r
+        Thread t = Thread.currentThread();\r
+        Context cx = (Context) threadContexts.get(t);\r
+        if (cx == null) {\r
+            throw new RuntimeException(\r
+                "No Context associated with current Thread");\r
+        }\r
+        return cx;\r
+    }\r
+\r
+    /** GCJ doesn't have an easy way to bundle up .properties files, so we do this */\r
+    static class HardCodedResourceBundle extends ListResourceBundle {\r
+        public Object[][] getContents() { return contents; }\r
+        static final Object[][] contents = {\r
+            { "msg.dup.parms", "Duplicate parameter name \"{0}\"." },\r
+            { "msg.ctor.not.found", "Constructor for \"{0}\" not found." },\r
+            { "msg.not.ctor", "\"{0}\" is not a constructor." },\r
+            { "msg.varargs.ctor", "Method or constructor \"{0}\" must be static with the signature  \"(Context cx, Object[] args, Function ctorObj, boolean inNewExpr)\"  to define a variable arguments constructor." },\r
+            { "msg.varargs.fun", "Method \"{0}\" must be static with the signature  \"(Context cx, Scriptable thisObj, Object[] args, Function funObj)\"  to define a variable arguments function." },\r
+            { "msg.incompat.call", "Method \"{0}\" called on incompatible object." },\r
+            { "msg.bad.parms", "Bad method parameters for \"{0}\"." },\r
+            { "msg.no.overload", "Method \"{0}\" occurs multiple times in class \"{1}\"." },\r
+            { "msg.method.not.found", "Method \"{0}\" not found in \"{1}\"." },\r
+            { "msg.bad.for.in.lhs", "Invalid left-hand side of for..in loop." },\r
+            { "msg.bad.lhs.assign", "Invalid assignment left-hand side." },\r
+            { "msg.mult.index", "Only one variable allowed in for..in loop." },\r
+            { "msg.cant.convert", "Can''t convert to type \"{0}\"." },\r
+            { "msg.cant.call.indirect", "Function \"{0}\" must be called directly, and not by way of a  function of another name." },\r
+            { "msg.eval.nonstring", "Calling eval() with anything other than a primitive string value will  simply return the value. Is this what you intended?" },\r
+            { "msg.only.from.new", "\"{0}\" may only be invoked from a \"new\" expression." },\r
+            { "msg.deprec.ctor", "The \"{0}\" constructor is deprecated." },\r
+            { "msg.no.function.ref.found.in", "no source found in {1} to decompile function reference {0}" },\r
+            { "msg.no.function.ref.found", "no source found to decompile function reference {0}" },\r
+            { "msg.arg.isnt.array", "second argument to Function.prototype.apply must be an array" },\r
+            { "msg.bad.esc.mask", "invalid string escape mask" },\r
+            { "msg.cant.instantiate", "error instantiating ({0}): class {1} is interface or abstract" },\r
+            { "msg.bad.ctor.sig", "Found constructor with wrong signature:  {0} calling {1} with signature {2}" },\r
+            { "msg.not.java.obj", "Expected argument to getClass() to be a Java object." },\r
+            { "msg.no.java.ctor", "Java constructor for \"{0}\" with arguments \"{1}\" not found." },\r
+            { "msg.method.ambiguous", "The choice of Java method {0}.{1} matching JavaScript argument types ({2}) is ambiguous;  candidate methods are: {3}" },\r
+            { "msg.constructor.ambiguous", "The choice of Java constructor {0} matching JavaScript argument types ({1}) is ambiguous;  candidate constructors are: {2}" },\r
+            { "msg.conversion.not.allowed", "Cannot convert {0} to {1}" },\r
+            { "msg.bad.quant", "Invalid quantifier {0}" },\r
+            { "msg.overlarge.max", "Overly large maximum {0}" },\r
+            { "msg.zero.quant", "Zero quantifier {0}" },\r
+            { "msg.max.lt.min", "Maximum {0} less than minimum" },\r
+            { "msg.unterm.quant", "Unterminated quantifier {0}" },\r
+            { "msg.unterm.paren", "Unterminated parenthetical {0}" },\r
+            { "msg.unterm.class", "Unterminated character class {0}" },\r
+            { "msg.bad.range", "Invalid range in character class." },\r
+            { "msg.trail.backslash", "Trailing \\ in regular expression." },\r
+            { "msg.no.regexp", "Regular expressions are not available." },\r
+            { "msg.bad.backref", "back-reference exceeds number of capturing parentheses." },\r
+            { "msg.dup.label", "Duplicate label {0}." },\r
+            { "msg.undef.label", "Undefined label {0}." },\r
+            { "msg.bad.break", "Unlabelled break must be inside loop or switch." },\r
+            { "msg.continue.outside", "continue must be inside loop." },\r
+            { "msg.continue.nonloop", "Can only continue to labeled iteration statement." },\r
+            { "msg.fn.redecl", "Function \"{0}\" redeclared; prior definition will be ignored." },\r
+            { "msg.no.paren.parms", "missing ( before function parameters" },\r
+            { "msg.no.parm", "missing formal parameter" },\r
+            { "msg.no.paren.after.parms", "missing ) after formal parameters" },\r
+            { "msg.no.brace.body", "missing '{' before function body" },\r
+            { "msg.no.brace.after.body", "missing } after function body" },\r
+            { "msg.no.paren.cond", "missing ( before condition" },\r
+            { "msg.no.paren.after.cond", "missing ) after condition" },\r
+            { "msg.no.semi.stmt", "missing ; before statement" },\r
+            { "msg.no.name.after.dot", "missing name after . operator" },\r
+            { "msg.no.bracket.index", "missing ] in index expression" },\r
+            { "msg.no.paren.switch", "missing ( before switch expression" },\r
+            { "msg.no.paren.after.switch", "missing ) after switch expression" },\r
+            { "msg.no.brace.switch", "missing '{' before switch body" },\r
+            { "msg.bad.switch", "invalid switch statement" },\r
+            { "msg.no.colon.case", "missing : after case expression" },\r
+            { "msg.no.while.do", "missing while after do-loop body" },\r
+            { "msg.no.paren.for", "missing ( after for" },\r
+            { "msg.no.semi.for", "missing ; after for-loop initializer" },\r
+            { "msg.no.semi.for.cond", "missing ; after for-loop condition" },\r
+            { "msg.no.paren.for.ctrl", "missing ) after for-loop control" },\r
+            { "msg.no.paren.with", "missing ( before with-statement object" },\r
+            { "msg.no.paren.after.with", "missing ) after with-statement object" },\r
+            { "msg.bad.return", "invalid return" },\r
+            { "msg.no.brace.block", "missing } in compound statement" },\r
+            { "msg.bad.label", "invalid label" },\r
+            { "msg.bad.var", "missing variable name" },\r
+            { "msg.bad.var.init", "invalid variable initialization" },\r
+            { "msg.no.colon.cond", "missing : in conditional expression" },\r
+            { "msg.no.paren.arg", "missing ) after argument list" },\r
+            { "msg.no.bracket.arg", "missing ] after element list" },\r
+            { "msg.bad.prop", "invalid property id" },\r
+            { "msg.no.colon.prop", "missing : after property id" },\r
+            { "msg.no.brace.prop", "missing } after property list" },\r
+            { "msg.no.paren", "missing ) in parenthetical" },\r
+            { "msg.reserved.id", "identifier is a reserved word" },\r
+            { "msg.no.paren.catch", "missing ( before catch-block condition" },\r
+            { "msg.bad.catchcond", "invalid catch block condition" },\r
+            { "msg.catch.unreachable", "any catch clauses following an unqualified catch are unreachable" },\r
+            { "msg.no.brace.catchblock", "missing '{' before catch-block body" },\r
+            { "msg.try.no.catchfinally", "''try'' without ''catch'' or ''finally''" },\r
+            { "msg.syntax", "syntax error" },\r
+            { "msg.assn.create", "Assignment to undefined \"{0}\" will create a new variable.  Add a variable statement at the top level scope to remove this warning." },\r
+            { "msg.prop.not.found", "Property not found." },\r
+            { "msg.invalid.type", "Invalid JavaScript value of type {0}" },\r
+            { "msg.primitive.expected", "Primitive type expected (had {0} instead)" },\r
+            { "msg.null.to.object", "Cannot convert null to an object." },\r
+            { "msg.undef.to.object", "Cannot convert undefined to an object." },\r
+            { "msg.cyclic.value", "Cyclic {0} value not allowed." },\r
+            { "msg.is.not.defined", "\"{0}\" is not defined." },\r
+            { "msg.isnt.function", "{0} is not a function." },\r
+            { "msg.bad.default.value", "Object''s getDefaultValue() method returned an object." },\r
+            { "msg.instanceof.not.object", " Can''t use instanceof on a non-object." },\r
+            { "msg.instanceof.bad.prototype", " ''prototype'' property of {0} is not an object." },\r
+            { "msg.bad.radix", " illegal radix {0}." },\r
+            { "msg.default.value", "Cannot find default value for object." },\r
+            { "msg.zero.arg.ctor", "Cannot load class \"{0}\" which has no zero-parameter constructor." },\r
+            { "msg.multiple.ctors", "Cannot have more than one constructor method, but found both {0} and {1}." },\r
+            { "msg.ctor.multiple.parms", "Can''t define constructor or class {0} since more than one  constructor has multiple parameters." },\r
+            { "msg.extend.scriptable", "{0} must extend ScriptableObject in order to define property {1}." },\r
+            { "msg.bad.getter.parms", "In order to define a property, getter {0} must have zero parameters  or a single ScriptableObject parameter." },\r
+            { "msg.obj.getter.parms", "Expected static or delegated getter {0} to take a ScriptableObject parameter." },\r
+            { "msg.getter.static", "Getter and setter must both be static or neither be static." },\r
+            { "msg.setter2.parms", "Two-parameter setter must take a ScriptableObject as its first parameter." },\r
+            { "msg.setter1.parms", "Expected single parameter setter for {0}" },\r
+            { "msg.setter2.expected", "Expected static or delegated setter {0} to take two parameters." },\r
+            { "msg.setter.parms", "Expected either one or two parameters for setter." },\r
+            { "msg.add.sealed", "Cannot add a property to a sealed object." },\r
+            { "msg.remove.sealed", "Cannot remove a property from a sealed object." },\r
+            { "msg.token.replaces.pushback", "ungot token {0} replaces pushback token {1}" },\r
+            { "msg.missing.exponent", "missing exponent" },\r
+            { "msg.caught.nfe", "number format error: {0}" },\r
+            { "msg.unterminated.string.lit", "unterminated string literal" },\r
+            { "msg.oct.esc.too.large", "octal escape too large" },\r
+            { "msg.nested.comment", "nested comment" },\r
+            { "msg.unterminated.comment", "unterminated comment" },\r
+            { "msg.unterminated.re.lit", "unterminated regular expression literal" },\r
+            { "msg.invalid.re.flag", "invalid flag after regular expression" },\r
+            { "msg.no.re.input.for", "no input for {0}" },\r
+            { "msg.illegal.character", "illegal character" },\r
+            { "msg.invalid.escape", "invalid Unicode escape sequence" },\r
+            { "msg.bad.octal.literal", "illegal octal literal digit {0}; interpreting it as a decimal digit" },\r
+            { "msg.undefined", "The undefined value has no properties." },\r
+            { "msg.java.internal.field.type", "Internal error: type conversion of {0} to assign to {1} on {2} failed." },\r
+            { "msg.java.conversion.implicit_method", "Can''t find converter method \"{0}\" on class {1}." },\r
+            { "msg.java.method.assign", "Java method \"{0}\" cannot be assigned to." },\r
+            { "msg.java.internal.private", "Internal error: attempt to access private/protected field \"{0}\"." },\r
+            { "msg.java.no_such_method", "Can''t find method {0}." },\r
+            { "msg.script.is.not.constructor", "Script objects are not constructors." },\r
+            { "msg.nonjava.method", "Java method \"{0}\" was invoked with a ''this'' value that was not a Java object." },\r
+            { "msg.java.member.not.found", "Java class \"{0}\" has no public instance field or method named \"{1}\"." },\r
+            { "msg.pkg.int", "Java package names may not be numbers." },\r
+            { "msg.ambig.import", "Ambiguous import: \"{0}\" and and \"{1}\"." },\r
+            { "msg.not.pkg", "Function importPackage must be called with a package; had \"{0}\" instead." },\r
+            { "msg.not.class", "Function importClass must be called with a class; had \"{0}\" instead." },\r
+            { "msg.prop.defined", "Cannot import \"{0}\" since a property by that name is already defined." },\r
+            { "msg.arraylength.bad", "Inappropriate array length." },\r
+            { "msg.bad.uri", "Malformed URI sequence." },\r
+            { "msg.bad.precision",     "Precision {0} out of range." }\r
+        };\r
+    }\r
+\r
+    static final ResourceBundle myresources = new HardCodedResourceBundle();\r
+\r
+    /* OPT there's a noticable delay for the first error!  Maybe it'd\r
+     * make sense to use a ListResourceBundle instead of a properties\r
+     * file to avoid (synchronized) text parsing.\r
+     */\r
+    static String getMessage(String messageId, Object[] arguments) {\r
+        Context cx = getCurrentContext();\r
+        Locale locale = cx != null ? cx.getLocale() : Locale.getDefault();\r
+\r
+        // ResourceBundle does cacheing.\r
+        ResourceBundle rb = myresources;\r
+\r
+        String formatString;\r
+        try {\r
+            formatString = rb.getString(messageId);\r
+        } catch (java.util.MissingResourceException mre) {\r
+            throw new RuntimeException\r
+                ("no message resource found for message property "+ messageId);\r
+        }\r
+\r
+        /*\r
+         * It's OK to format the string, even if 'arguments' is null;\r
+         * we need to format it anyway, to make double ''s collapse to\r
+         * single 's.\r
+         */\r
+        // TODO: MessageFormat is not available on pJava\r
+        MessageFormat formatter = new MessageFormat(formatString);\r
+        return formatter.format(arguments);\r
+    }\r
+\r
+    // debug flags\r
+    static final boolean printTrees = false;\r
+    \r
+    /**\r
+     * Compile a script.\r
+     *\r
+     * Reads script source from the reader and compiles it, returning\r
+     * a class for either the script or the function depending on the\r
+     * value of <code>returnFunction</code>.\r
+     *\r
+     * @param scope the scope to compile relative to\r
+     * @param in the Reader to read source from\r
+     * @param sourceName the name of the origin of the source (usually\r
+     *                   a file or URL)\r
+     * @param lineno the line number of the start of the source\r
+     * @param securityDomain an arbitrary object that specifies security \r
+     *        information about the origin or owner of the script. For \r
+     *        implementations that don't care about security, this value \r
+     *        may be null.\r
+     * @param returnFunction if true, will expect the source to contain\r
+     *                        a function; return value is assumed to\r
+     *                        then be a org.mozilla.javascript.Function\r
+     * @return a class for the script or function\r
+     * @see org.mozilla.javascript.Context#compileReader\r
+     */\r
+    private Object compile(Scriptable scope, Reader in, String sourceName, \r
+                           int lineno, Object securityDomain, \r
+                           boolean returnFunction)\r
+        throws IOException\r
+    {\r
+        if (debugger != null && in != null) {\r
+            in = new DebugReader(in);\r
+        }\r
+        TokenStream ts = new TokenStream(in, scope, sourceName, lineno);\r
+        return compile(scope, ts, securityDomain, in, returnFunction);\r
+    }\r
+\r
+    private static Class codegenClass = null;\r
+    private static ClassNameHelper nameHelper = null;\r
+    static {\r
+        /*\r
+        try {\r
+            codegenClass = Class.forName(\r
+                "org.mozilla.javascript.optimizer.Codegen");\r
+            Class nameHelperClass = Class.forName(\r
+                "org.mozilla.javascript.optimizer.OptClassNameHelper");\r
+            nameHelper = (ClassNameHelper)nameHelperClass.newInstance();\r
+        } catch (ClassNotFoundException x) {\r
+            // ...must be running lite, that's ok\r
+            codegenClass = null;\r
+        } catch (IllegalAccessException x) {\r
+            codegenClass = null;\r
+        } catch (InstantiationException x) {\r
+            codegenClass = null;\r
+        }\r
+        */\r
+    }\r
+    \r
+    private Interpreter getCompiler() {\r
+        if (codegenClass != null) {\r
+            try {\r
+                return (Interpreter) codegenClass.newInstance();\r
+            }\r
+            catch (SecurityException x) {   \r
+            }\r
+            catch (IllegalArgumentException x) {\r
+            }\r
+            catch (InstantiationException x) {\r
+            }\r
+            catch (IllegalAccessException x) {\r
+            }\r
+            // fall through\r
+        }\r
+        return new Interpreter();\r
+    }\r
+    \r
+    private Object compile(Scriptable scope, TokenStream ts, \r
+                           Object securityDomain, Reader in,\r
+                           boolean returnFunction)\r
+        throws IOException\r
+    {\r
+        Interpreter compiler = optimizationLevel == -1 \r
+                               ? new Interpreter()\r
+                               : getCompiler();\r
+        \r
+        errorCount = 0;\r
+        IRFactory irf = compiler.createIRFactory(ts, nameHelper, scope);\r
+        Parser p = new Parser(irf);\r
+        Node tree = (Node) p.parse(ts);\r
+        if (tree == null) \r
+            return null;\r
+\r
+        tree = compiler.transform(tree, ts, scope);\r
+\r
+        if (printTrees)\r
+            System.out.println(tree.toStringTree());\r
+        \r
+        if (returnFunction) {\r
+            Node first = tree.getFirstChild();\r
+            if (first == null)\r
+                return null;\r
+            tree = (Node) first.getProp(Node.FUNCTION_PROP);\r
+            if (tree == null)\r
+                return null;\r
+        }\r
+        \r
+        if (in instanceof DebugReader) {\r
+            DebugReader dr = (DebugReader) in;\r
+            tree.putProp(Node.DEBUGSOURCE_PROP, dr.getSaved());\r
+        }\r
+\r
+        Object result = compiler.compile(this, scope, tree, securityDomain,\r
+                                         securitySupport, nameHelper);\r
+\r
+        return errorCount == 0 ? result : null;\r
+    }\r
+\r
+    static String getSourcePositionFromStack(int[] linep) {\r
+        Context cx = getCurrentContext();\r
+        if (cx == null)\r
+            return null;\r
+        if (cx.interpreterLine > 0 && cx.interpreterSourceFile != null) {\r
+            linep[0] = cx.interpreterLine;\r
+            return cx.interpreterSourceFile;\r
+        }\r
+        /**\r
+         * A bit of a hack, but the only way to get filename and line\r
+         * number from an enclosing frame.\r
+         */\r
+        CharArrayWriter writer = new CharArrayWriter();\r
+        RuntimeException re = new RuntimeException();\r
+        re.printStackTrace(new PrintWriter(writer));\r
+        String s = writer.toString();\r
+        int open = -1;\r
+        int close = -1;\r
+        int colon = -1;\r
+        for (int i=0; i < s.length(); i++) {\r
+            char c = s.charAt(i);\r
+            if (c == ':')\r
+                colon = i;\r
+            else if (c == '(')\r
+                open = i;\r
+            else if (c == ')')\r
+                close = i;\r
+            else if (c == '\n' && open != -1 && close != -1 && colon != -1 && \r
+                     open < colon && colon < close) \r
+            {\r
+                String fileStr = s.substring(open + 1, colon);\r
+                if (fileStr.endsWith(".js")) {\r
+                    String lineStr = s.substring(colon + 1, close);\r
+                    try {\r
+                        linep[0] = Integer.parseInt(lineStr);\r
+                        return fileStr;\r
+                    }\r
+                    catch (NumberFormatException e) {\r
+                        // fall through\r
+                    }\r
+                }\r
+                open = close = colon = -1;\r
+            }\r
+        }\r
+\r
+        return null;\r
+    }\r
+\r
+    RegExpProxy getRegExpProxy() {\r
+        if (regExpProxy == null) {\r
+            try {\r
+                Class c = Class.forName(\r
+                            "org.mozilla.javascript.regexp.RegExpImpl");\r
+                regExpProxy = (RegExpProxy) c.newInstance();\r
+                return regExpProxy;\r
+            } catch (ClassNotFoundException e) {\r
+            } catch (InstantiationException e) {\r
+            } catch (IllegalAccessException e) {\r
+            }\r
+        }\r
+        return regExpProxy;\r
+    }\r
+\r
+    private void newArrayHelper(Scriptable scope, Scriptable array) {\r
+        array.setParentScope(scope);\r
+        Object ctor = ScriptRuntime.getTopLevelProp(scope, "Array");\r
+        if (ctor != null && ctor instanceof Scriptable) {\r
+            Scriptable s = (Scriptable) ctor;\r
+            array.setPrototype((Scriptable) s.get("prototype", s));\r
+        }\r
+    }\r
+    \r
+    final boolean isVersionECMA1() {\r
+        return version == VERSION_DEFAULT || version >= VERSION_1_3;\r
+    }\r
+\r
+    /**\r
+     * Get the security context from the given class.\r
+     * <p>\r
+     * When some form of security check needs to be done, the class context\r
+     * must retrieved from the security manager to determine what class is\r
+     * requesting some form of privileged access. \r
+     * @since 1.4 release 2\r
+     */\r
+    Object getSecurityDomainFromClass(Class cl) {\r
+        if (cl == Interpreter.class)\r
+            return interpreterSecurityDomain;\r
+        return securitySupport.getSecurityDomain(cl);\r
+    }\r
+    \r
+    SecuritySupport getSecuritySupport() {\r
+        return securitySupport;\r
+    }\r
+    \r
+    Object getSecurityDomainForStackDepth(int depth) {\r
+        Object result = null;\r
+        if (securitySupport != null) {\r
+            Class[] classes = securitySupport.getClassContext();\r
+            if (classes != null) {\r
+                if (depth != -1) {\r
+                    int depth1 = depth + 1;\r
+                    result = getSecurityDomainFromClass(classes[depth1]);\r
+                } else {\r
+                    for (int i=1; i < classes.length; i++) {\r
+                        result = getSecurityDomainFromClass(classes[i]);\r
+                        if (result != null)\r
+                            break;\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        if (result != null)\r
+            return result;\r
+        if (requireSecurityDomain) \r
+            checkSecurityDomainRequired();\r
+        return null;\r
+    }\r
+\r
+    private static boolean requireSecurityDomain = false;\r
+    private static boolean resourceMissing = false;\r
+\r
+    final static String securityResourceName = "org.mozilla.javascript.resources.Security";\r
+    \r
+    final public static void checkSecurityDomainRequired() { }\r
+\r
+    public boolean isGeneratingDebugChanged() {\r
+        return generatingDebugChanged;\r
+    }\r
+    \r
+\r
+    /**\r
+     * Add a name to the list of names forcing the creation of real\r
+     * activation objects for functions.\r
+     *\r
+     * @param name the name of the object to add to the list\r
+     */\r
+    public void addActivationName(String name) {\r
+        if (activationNames == null) \r
+            activationNames = new Hashtable(5);\r
+        activationNames.put(name, name);\r
+    }\r
+\r
+    /**\r
+     * Check whether the name is in the list of names of objects\r
+     * forcing the creation of activation objects.\r
+     *\r
+     * @param name the name of the object to test\r
+     *\r
+     * @return true if an function activation object is needed.\r
+     */\r
+    public boolean isActivationNeeded(String name) {\r
+        if ("arguments".equals(name))\r
+            return true;\r
+        return activationNames != null && activationNames.containsKey(name);\r
+    }\r
+\r
+    /**\r
+     * Remove a name from the list of names forcing the creation of real\r
+     * activation objects for functions.\r
+     *\r
+     * @param name the name of the object to remove from the list\r
+     */\r
+    public void removeActivationName(String name) {\r
+        if (activationNames != null)\r
+            activationNames.remove(name);\r
+    }\r
+\r
+\r
+    static final boolean useJSObject = false;\r
+\r
+    /** \r
+     * The activation of the currently executing function or script. \r
+     */\r
+    NativeCall currentActivation;\r
+\r
+    // for Objects, Arrays to tag themselves as being printed out,\r
+    // so they don't print themselves out recursively.\r
+    Hashtable iterating;\r
+            \r
+    Object interpreterSecurityDomain;\r
+\r
+    int version;\r
+    int errorCount;\r
+    static boolean isCachingEnabled = true;\r
+    \r
+    private SecuritySupport securitySupport;\r
+    private ErrorReporter errorReporter;\r
+    private Thread currentThread;\r
+    private static Hashtable threadContexts = new Hashtable(11);\r
+    private RegExpProxy regExpProxy;\r
+    private Locale locale;\r
+    private boolean generatingDebug;\r
+    private boolean generatingDebugChanged;\r
+    private boolean generatingSource=true;\r
+    private boolean compileFunctionsWithDynamicScopeFlag;\r
+    private int optimizationLevel;\r
+    WrapHandler wrapHandler;\r
+    Debugger debugger;\r
+    DebuggableEngine debuggableEngine;\r
+    boolean inLineStepMode;\r
+    java.util.Stack frameStack;    \r
+    private int enterCount;\r
+    private Object[] listeners;\r
+    private Hashtable hashtable;\r
+\r
+    /**\r
+     * This is the list of names of objects forcing the creation of\r
+     * function activation records.\r
+     */\r
+    private Hashtable activationNames;\r
+\r
+    // Private lock for static fields to avoid a possibility of denial\r
+    // of service via synchronized (Context.class) { while (true) {} }\r
+    private static final Object staticDataLock = new Object();\r
+    private static Object[] contextListeners;\r
+    public Function currentFunction;\r
+    Vector arrayCache = new Vector(10);\r
+\r
+    // For the interpreter to indicate line/source for error reports.\r
+    public int interpreterLine;\r
+    public String interpreterSourceFile;\r
+\r
+    public int stackDepth = 0;\r
+\r
+    // For instruction counting (interpreter only)\r
+    int instructionCount;\r
+    int instructionThreshold;\r
+}\r
diff --git a/src/org/mozilla/javascript/ContextListener.java b/src/org/mozilla/javascript/ContextListener.java
new file mode 100644 (file)
index 0000000..fb8c040
--- /dev/null
@@ -0,0 +1,53 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * Embeddings that wish to \r
+ * @see org.mozilla.javascript.Context#addContextListener\r
+ */\r
+public interface ContextListener {\r
+\r
+    public void contextCreated(Context cx);\r
+    \r
+    public void contextEntered(Context cx);\r
+    \r
+    public void contextExited(Context cx);\r
+    \r
+    public void contextReleased(Context cx);\r
+}\r
diff --git a/src/org/mozilla/javascript/DToA.java b/src/org/mozilla/javascript/DToA.java
new file mode 100644 (file)
index 0000000..76cde25
--- /dev/null
@@ -0,0 +1,1197 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Waldemar Horwat\r
+ * Roger Lawrence\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.math.BigInteger;\r
+\r
+class DToA {\r
+\r
+\r
+/* "-0.0000...(1073 zeros after decimal point)...0001\0" is the longest string that we could produce,\r
+ * which occurs when printing -5e-324 in binary.  We could compute a better estimate of the size of\r
+ * the output string and malloc fewer bytes depending on d and base, but why bother? */\r
+\r
+    static final int DTOBASESTR_BUFFER_SIZE = 1078;\r
+\r
+    static char BASEDIGIT(int digit) {\r
+        return (char)((digit >= 10) ? 'a' - 10 + digit : '0' + digit);\r
+    }\r
+       \r
+    static final int\r
+               DTOSTR_STANDARD = 0,              /* Either fixed or exponential format; round-trip */\r
+               DTOSTR_STANDARD_EXPONENTIAL = 1,  /* Always exponential format; round-trip */\r
+               DTOSTR_FIXED = 2,                 /* Round to <precision> digits after the decimal point; exponential if number is large */\r
+               DTOSTR_EXPONENTIAL = 3,           /* Always exponential format; <precision> significant digits */\r
+               DTOSTR_PRECISION = 4;             /* Either fixed or exponential format; <precision> significant digits */\r
+       \r
+\r
+    static final int Frac_mask = 0xfffff;\r
+    static final int Exp_shift = 20;\r
+    static final int Exp_msk1 = 0x100000;\r
+    static final int Bias = 1023;\r
+    static final int P = 53;\r
+\r
+    static final int Exp_shift1 = 20;\r
+    static final int Exp_mask  = 0x7ff00000;\r
+    static final int Bndry_mask  = 0xfffff;\r
+    static final int Log2P = 1;\r
+\r
+    static final int Sign_bit = 0x80000000;\r
+    static final int Exp_11  = 0x3ff00000;\r
+       static final int Ten_pmax = 22;\r
+       static final int Quick_max = 14;\r
+       static final int Bletch = 0x10;\r
+       static final int Frac_mask1 = 0xfffff;\r
+       static final int Int_max = 14;\r
+       static final int n_bigtens = 5;\r
+\r
+       \r
+       static final double tens[] = {\r
+           1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,\r
+           1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,\r
+           1e20, 1e21, 1e22\r
+       };\r
+       \r
+       static final double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 };\r
+\r
+    static int lo0bits(int y)\r
+    {\r
+        int k;\r
+        int x = y;\r
+\r
+        if ((x & 7) != 0) {\r
+            if ((x & 1) != 0)\r
+                return 0;\r
+            if ((x & 2) != 0) {\r
+                return 1;\r
+            }\r
+            return 2;\r
+        }\r
+        k = 0;\r
+        if ((x & 0xffff) == 0) {\r
+            k = 16;\r
+            x >>>= 16;\r
+        }\r
+        if ((x & 0xff) == 0) {\r
+            k += 8;\r
+            x >>>= 8;\r
+        }\r
+        if ((x & 0xf) == 0) {\r
+            k += 4;\r
+            x >>>= 4;\r
+        }\r
+        if ((x & 0x3) == 0) {\r
+            k += 2;\r
+            x >>>= 2;\r
+        }\r
+        if ((x & 1) == 0) {\r
+            k++;\r
+            x >>>= 1;\r
+            if ((x & 1) == 0)\r
+                return 32;\r
+        }\r
+        return k;\r
+    }\r
+\r
+       /* Return the number (0 through 32) of most significant zero bits in x. */\r
+       static int hi0bits(int x)\r
+       {\r
+           int k = 0;\r
+\r
+           if ((x & 0xffff0000) == 0) {\r
+               k = 16;\r
+               x <<= 16;\r
+           }\r
+           if ((x & 0xff000000) == 0) {\r
+               k += 8;\r
+               x <<= 8;\r
+           }\r
+           if ((x & 0xf0000000) == 0) {\r
+               k += 4;\r
+               x <<= 4;\r
+           }\r
+           if ((x & 0xc0000000) == 0) {\r
+               k += 2;\r
+               x <<= 2;\r
+           }\r
+           if ((x & 0x80000000) == 0) {\r
+               k++;\r
+               if ((x & 0x40000000) == 0)\r
+                   return 32;\r
+           }\r
+           return k;\r
+       }\r
+\r
+       static void stuffBits(byte bits[], int offset, int val)\r
+    {\r
+        bits[offset] = (byte)(val >> 24);\r
+        bits[offset + 1] = (byte)(val >> 16);\r
+        bits[offset + 2] = (byte)(val >> 8);\r
+        bits[offset + 3] = (byte)(val);\r
+    }\r
+\r
+       /* Convert d into the form b*2^e, where b is an odd integer.  b is the returned\r
+        * Bigint and e is the returned binary exponent.  Return the number of significant\r
+        * bits in b in bits.  d must be finite and nonzero. */\r
+    static BigInteger d2b(double d, int[] e, int[] bits)\r
+    {\r
+        byte dbl_bits[];\r
+        int i, k, y, z, de;\r
+        long dBits = Double.doubleToLongBits(d);\r
+        int d0 = (int)(dBits >>> 32);\r
+        int d1 = (int)(dBits);\r
+\r
+        z = d0 & Frac_mask;\r
+        d0 &= 0x7fffffff;   /* clear sign bit, which we ignore */\r
+\r
+        if ((de = (int)(d0 >>> Exp_shift)) != 0)\r
+            z |= Exp_msk1;\r
+\r
+        if ((y = d1) != 0) {\r
+               dbl_bits = new byte[8];\r
+            k = lo0bits(y);\r
+            y >>>= k;\r
+            if (k != 0) {\r
+                stuffBits(dbl_bits, 4, y | z << (32 - k));\r
+                z >>= k;\r
+            }\r
+            else\r
+                stuffBits(dbl_bits, 4, y);\r
+            stuffBits(dbl_bits, 0, z);\r
+                       i = (z != 0) ? 2 : 1;\r
+        }\r
+        else {\r
+    //        JS_ASSERT(z);\r
+                       dbl_bits = new byte[4];\r
+            k = lo0bits(z);\r
+            z >>>= k;\r
+            stuffBits(dbl_bits, 0, z);\r
+            k += 32;\r
+                       i = 1;\r
+        }\r
+        if (de != 0) {\r
+            e[0] = de - Bias - (P-1) + k;\r
+               bits[0] = P - k;\r
+        }\r
+        else {\r
+            e[0] = de - Bias - (P-1) + 1 + k;\r
+               bits[0] = 32*i - hi0bits(z);\r
+        }\r
+        return new BigInteger(dbl_bits);\r
+       }\r
+\r
+    public static String JS_dtobasestr(int base, double d)\r
+    {\r
+        char[] buffer;       /* The output string */\r
+        int p;               /* index to current position in the buffer */\r
+        int pInt;            /* index to the beginning of the integer part of the string */\r
+\r
+        int q;\r
+        int digit;\r
+        double di;           /* d truncated to an integer */\r
+        double df;           /* The fractional part of d */\r
+\r
+//        JS_ASSERT(base >= 2 && base <= 36);\r
+\r
+        buffer = new char[DTOBASESTR_BUFFER_SIZE];\r
+\r
+        p = 0;\r
+        if (d < 0.0) {\r
+            buffer[p++] = '-';\r
+            d = -d;\r
+        }\r
+\r
+        /* Check for Infinity and NaN */\r
+        if (Double.isNaN(d))\r
+            return "NaN";\r
+        else\r
+            if (Double.isInfinite(d))\r
+                return "Infinity";\r
+\r
+        /* Output the integer part of d with the digits in reverse order. */\r
+        pInt = p;\r
+        di = (int)d;\r
+        BigInteger b = BigInteger.valueOf((int)di);\r
+        String intDigits = b.toString(base);\r
+        intDigits.getChars(0, intDigits.length(), buffer, p);\r
+        p += intDigits.length();\r
+\r
+        df = d - di;\r
+        if (df != 0.0) {\r
+            /* We have a fraction. */\r
+            buffer[p++] = '.';\r
+\r
+            long dBits = Double.doubleToLongBits(d);\r
+            int word0 = (int)(dBits >> 32);\r
+            int word1 = (int)(dBits);\r
+\r
+            int[] e = new int[1];\r
+                       int[] bbits = new int[1];\r
+                                        \r
+                       b = d2b(df, e, bbits);\r
+//            JS_ASSERT(e < 0);\r
+            /* At this point df = b * 2^e.  e must be less than zero because 0 < df < 1. */\r
+\r
+            int s2 = -(word0 >>> Exp_shift1 & Exp_mask >> Exp_shift1);\r
+            if (s2 == 0)\r
+                s2 = -1;\r
+            s2 += Bias + P;\r
+            /* 1/2^s2 = (nextDouble(d) - d)/2 */\r
+//            JS_ASSERT(-s2 < e);\r
+            BigInteger mlo = BigInteger.valueOf(1);\r
+            BigInteger mhi = mlo;\r
+            if ((word1 == 0) && ((word0 & Bndry_mask) == 0)\r
+                && ((word0 & (Exp_mask & Exp_mask << 1)) != 0)) {\r
+                /* The special case.  Here we want to be within a quarter of the last input\r
+                   significant digit instead of one half of it when the output string's value is less than d.  */\r
+                s2 += Log2P;\r
+                mhi = BigInteger.valueOf(1<<Log2P);\r
+            }\r
+\r
+            b = b.shiftLeft(e[0] + s2);\r
+            BigInteger s = BigInteger.valueOf(1);\r
+            s = s.shiftLeft(s2);\r
+            /* At this point we have the following:\r
+             *   s = 2^s2;\r
+             *   1 > df = b/2^s2 > 0;\r
+             *   (d - prevDouble(d))/2 = mlo/2^s2;\r
+             *   (nextDouble(d) - d)/2 = mhi/2^s2. */\r
+            BigInteger bigBase = BigInteger.valueOf(base);\r
+\r
+            boolean done = false;\r
+            do {\r
+                b = b.multiply(bigBase);\r
+                BigInteger[] divResult = b.divideAndRemainder(s);\r
+                b = divResult[1];\r
+                digit = (char)(divResult[0].intValue());\r
+                if (mlo == mhi)\r
+                    mlo = mhi = mlo.multiply(bigBase);\r
+                else {\r
+                    mlo = mlo.multiply(bigBase);\r
+                    mhi = mhi.multiply(bigBase);\r
+                }\r
+\r
+                /* Do we yet have the shortest string that will round to d? */\r
+                int j = b.compareTo(mlo);\r
+                /* j is b/2^s2 compared with mlo/2^s2. */\r
+                BigInteger delta = s.subtract(mhi);\r
+                int j1 = (delta.signum() <= 0) ? 1 : b.compareTo(delta);\r
+                /* j1 is b/2^s2 compared with 1 - mhi/2^s2. */\r
+                if (j1 == 0 && ((word1 & 1) == 0)) {\r
+                    if (j > 0)\r
+                        digit++;\r
+                    done = true;\r
+                } else\r
+                if (j < 0 || (j == 0 && ((word1 & 1) == 0))) {\r
+                    if (j1 > 0) {\r
+                        /* Either dig or dig+1 would work here as the least significant digit.\r
+                           Use whichever would produce an output value closer to d. */\r
+                        b = b.shiftLeft(1);\r
+                        j1 = b.compareTo(s);\r
+                        if (j1 > 0) /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output\r
+                                     * such as 3.5 in base 3.  */\r
+                            digit++;\r
+                    }\r
+                    done = true;\r
+                } else if (j1 > 0) {\r
+                    digit++;\r
+                    done = true;\r
+                }\r
+//                JS_ASSERT(digit < (uint32)base);\r
+                buffer[p++] = BASEDIGIT(digit);\r
+            } while (!done);\r
+        }\r
+\r
+        return new String(buffer, 0, p);\r
+    }\r
+\r
+       /* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.\r
+        *\r
+        * Inspired by "How to Print Floating-Point Numbers Accurately" by\r
+        * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101].\r
+        *\r
+        * Modifications:\r
+        *  1. Rather than iterating, we use a simple numeric overestimate\r
+        *     to determine k = floor(log10(d)).  We scale relevant\r
+        *     quantities using O(log2(k)) rather than O(k) multiplications.\r
+        *  2. For some modes > 2 (corresponding to ecvt and fcvt), we don't\r
+        *     try to generate digits strictly left to right.  Instead, we\r
+        *     compute with fewer bits and propagate the carry if necessary\r
+        *     when rounding the final digit up.  This is often faster.\r
+        *  3. Under the assumption that input will be rounded nearest,\r
+        *     mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.\r
+        *     That is, we allow equality in stopping tests when the\r
+        *     round-nearest rule will give the same floating-point value\r
+        *     as would satisfaction of the stopping test with strict\r
+        *     inequality.\r
+        *  4. We remove common factors of powers of 2 from relevant\r
+        *     quantities.\r
+        *  5. When converting floating-point integers less than 1e16,\r
+        *     we use floating-point arithmetic rather than resorting\r
+        *     to multiple-precision integers.\r
+        *  6. When asked to produce fewer than 15 digits, we first try\r
+        *     to get by with floating-point arithmetic; we resort to\r
+        *     multiple-precision integer arithmetic only if we cannot\r
+        *     guarantee that the floating-point calculation has given\r
+        *     the correctly rounded result.  For k requested digits and\r
+        *     "uniformly" distributed input, the probability is\r
+        *     something like 10^(k-15) that we must resort to the Long\r
+        *     calculation.\r
+        */\r
+\r
+       static int word0(double d)\r
+       {\r
+        long dBits = Double.doubleToLongBits(d);\r
+        return (int)(dBits >> 32);\r
+       }\r
+       \r
+       static double setWord0(double d, int i)\r
+       {\r
+        long dBits = Double.doubleToLongBits(d);\r
+        dBits = ((long)i << 32) | (dBits & 0x0FFFFFFFFL);\r
+               return Double.longBitsToDouble(dBits);\r
+       }\r
+       \r
+       static int word1(double d)\r
+       {\r
+        long dBits = Double.doubleToLongBits(d);\r
+        return (int)(dBits);\r
+       }\r
+       \r
+       /* Return b * 5^k.  k must be nonnegative. */\r
+       // XXXX the C version built a cache of these\r
+       static BigInteger pow5mult(BigInteger b, int k)\r
+       {\r
+               return b.multiply(BigInteger.valueOf(5).pow(k));\r
+       }\r
+       \r
+       static boolean roundOff(StringBuffer buf)\r
+       {\r
+               char lastCh;\r
+               while ((lastCh = buf.charAt(buf.length() - 1)) == '9') {\r
+                       buf.setLength(buf.length() - 1);\r
+                       if (buf.length() == 0) {\r
+                               return true;\r
+                       }\r
+               }\r
+               buf.append((char)(lastCh + 1));\r
+               return false;\r
+       }\r
+       \r
+       /* Always emits at least one digit. */\r
+       /* If biasUp is set, then rounding in modes 2 and 3 will round away from zero\r
+        * when the number is exactly halfway between two representable values.  For example, \r
+        * rounding 2.5 to zero digits after the decimal point will return 3 and not 2.\r
+        * 2.49 will still round to 2, and 2.51 will still round to 3. */\r
+       /* bufsize should be at least 20 for modes 0 and 1.  For the other modes,\r
+        * bufsize should be two greater than the maximum number of output characters expected. */\r
+       static int\r
+       JS_dtoa(double d, int mode, boolean biasUp, int ndigits,\r
+                                       boolean[] sign, StringBuffer buf)\r
+       {\r
+           /*  Arguments ndigits, decpt, sign are similar to those\r
+               of ecvt and fcvt; trailing zeros are suppressed from\r
+               the returned string.  If not null, *rve is set to point\r
+               to the end of the return value.  If d is +-Infinity or NaN,\r
+               then *decpt is set to 9999.\r
+\r
+               mode:\r
+               0 ==> shortest string that yields d when read in\r
+               and rounded to nearest.\r
+               1 ==> like 0, but with Steele & White stopping rule;\r
+               e.g. with IEEE P754 arithmetic , mode 0 gives\r
+               1e23 whereas mode 1 gives 9.999999999999999e22.\r
+               2 ==> max(1,ndigits) significant digits.  This gives a\r
+               return value similar to that of ecvt, except\r
+               that trailing zeros are suppressed.\r
+               3 ==> through ndigits past the decimal point.  This\r
+               gives a return value similar to that from fcvt,\r
+               except that trailing zeros are suppressed, and\r
+               ndigits can be negative.\r
+               4-9 should give the same return values as 2-3, i.e.,\r
+               4 <= mode <= 9 ==> same return as mode\r
+               2 + (mode & 1).  These modes are mainly for\r
+               debugging; often they run slower but sometimes\r
+               faster than modes 2-3.\r
+               4,5,8,9 ==> left-to-right digit generation.\r
+               6-9 ==> don't try fast floating-point estimate\r
+               (if applicable).\r
+\r
+               Values of mode other than 0-9 are treated as mode 0.\r
+\r
+               Sufficient space is allocated to the return value\r
+               to hold the suppressed trailing zeros.\r
+           */\r
+\r
+           int b2, b5, i, ieps, ilim, ilim0, ilim1,\r
+               j, j1, k, k0, m2, m5, s2, s5;\r
+               char dig;\r
+           long L;\r
+           long x;\r
+           BigInteger b, b1, delta, mlo, mhi, S;\r
+               int[] be = new int[1];\r
+               int[] bbits = new int[1];\r
+           double d2, ds, eps;\r
+               boolean spec_case, denorm, k_check, try_quick, leftright;\r
+\r
+           if ((word0(d) & Sign_bit) != 0) {\r
+               /* set sign for everything, including 0's and NaNs */\r
+               sign[0] = true;\r
+               // word0(d) &= ~Sign_bit;  /* clear sign bit */\r
+                       d = setWord0(d, word0(d) & ~Sign_bit);\r
+           }\r
+           else\r
+               sign[0] = false;\r
+\r
+           if ((word0(d) & Exp_mask) == Exp_mask) {\r
+               /* Infinity or NaN */\r
+               buf.append(((word1(d) == 0) && ((word0(d) & Frac_mask) == 0)) ? "Infinity" : "NaN");\r
+               return 9999;\r
+           }\r
+           if (d == 0) {\r
+//           no_digits:\r
+                       buf.setLength(0);\r
+                       buf.append('0');                /* copy "0" to buffer */\r
+               return 1;\r
+           }\r
+        \r
+               b = d2b(d, be, bbits);\r
+               if ((i = (int)(word0(d) >>> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) {\r
+                       d2 = setWord0(d, (word0(d) & Frac_mask1) | Exp_11);\r
+               /* log(x)   ~=~ log(1.5) + (x-1.5)/1.5\r
+                * log10(x)  =  log(x) / log(10)\r
+                *      ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))\r
+                * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2)\r
+                *\r
+                * This suggests computing an approximation k to log10(d) by\r
+                *\r
+                * k = (i - Bias)*0.301029995663981\r
+                *  + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );\r
+                *\r
+                * We want k to be too large rather than too small.\r
+                * The error in the first-order Taylor series approximation\r
+                * is in our favor, so we just round up the constant enough\r
+                * to compensate for any error in the multiplication of\r
+                * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,\r
+                * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,\r
+                * adding 1e-13 to the constant term more than suffices.\r
+                * Hence we adjust the constant term to 0.1760912590558.\r
+                * (We could get a more accurate k by invoking log10,\r
+                *  but this is probably not worthwhile.)\r
+                */\r
+               i -= Bias;\r
+               denorm = false;\r
+           }\r
+           else {\r
+               /* d is denormalized */\r
+               i = bbits[0] + be[0] + (Bias + (P-1) - 1);\r
+               x = (i > 32) ? word0(d) << (64 - i) | word1(d) >>> (i - 32) : word1(d) << (32 - i);\r
+//             d2 = x;\r
+//             word0(d2) -= 31*Exp_msk1; /* adjust exponent */\r
+                       d2 = setWord0(x, word0(x) - 31*Exp_msk1);\r
+               i -= (Bias + (P-1) - 1) + 1;\r
+               denorm = true;\r
+           }\r
+           /* At this point d = f*2^i, where 1 <= f < 2.  d2 is an approximation of f. */\r
+           ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;\r
+           k = (int)ds;\r
+           if (ds < 0.0 && ds != k)\r
+               k--;    /* want k = floor(ds) */\r
+           k_check = true;\r
+           if (k >= 0 && k <= Ten_pmax) {\r
+               if (d < tens[k])\r
+                   k--;\r
+               k_check = false;\r
+           }\r
+           /* At this point floor(log10(d)) <= k <= floor(log10(d))+1.\r
+              If k_check is zero, we're guaranteed that k = floor(log10(d)). */\r
+           j = bbits[0] - i - 1;\r
+           /* At this point d = b/2^j, where b is an odd integer. */\r
+           if (j >= 0) {\r
+               b2 = 0;\r
+               s2 = j;\r
+           }\r
+           else {\r
+               b2 = -j;\r
+               s2 = 0;\r
+           }\r
+           if (k >= 0) {\r
+               b5 = 0;\r
+               s5 = k;\r
+               s2 += k;\r
+           }\r
+           else {\r
+               b2 -= k;\r
+               b5 = -k;\r
+               s5 = 0;\r
+           }\r
+           /* At this point d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5), where b is an odd integer,\r
+              b2 >= 0, b5 >= 0, s2 >= 0, and s5 >= 0. */\r
+           if (mode < 0 || mode > 9)\r
+               mode = 0;\r
+           try_quick = true;\r
+           if (mode > 5) {\r
+               mode -= 4;\r
+               try_quick = false;\r
+           }\r
+           leftright = true;\r
+           ilim = ilim1 = 0;\r
+           switch(mode) {\r
+                       case 0:\r
+                       case 1:\r
+                           ilim = ilim1 = -1;\r
+                           i = 18;\r
+                           ndigits = 0;\r
+                           break;\r
+                       case 2:\r
+                           leftright = false;\r
+                           /* no break */\r
+                       case 4:\r
+                           if (ndigits <= 0)\r
+                               ndigits = 1;\r
+                           ilim = ilim1 = i = ndigits;\r
+                           break;\r
+                       case 3:\r
+                           leftright = false;\r
+                           /* no break */\r
+                       case 5:\r
+                           i = ndigits + k + 1;\r
+                           ilim = i;\r
+                           ilim1 = i - 1;\r
+                           if (i <= 0)\r
+                               i = 1;\r
+           }\r
+           /* ilim is the maximum number of significant digits we want, based on k and ndigits. */\r
+           /* ilim1 is the maximum number of significant digits we want, based on k and ndigits,\r
+              when it turns out that k was computed too high by one. */\r
+\r
+               boolean fast_failed = false;\r
+           if (ilim >= 0 && ilim <= Quick_max && try_quick) {\r
+\r
+               /* Try to get by with floating-point arithmetic. */\r
+\r
+               i = 0;\r
+               d2 = d;\r
+               k0 = k;\r
+               ilim0 = ilim;\r
+               ieps = 2; /* conservative */\r
+               /* Divide d by 10^k, keeping track of the roundoff error and avoiding overflows. */\r
+               if (k > 0) {\r
+                   ds = tens[k&0xf];\r
+                   j = k >> 4;\r
+                   if ((j & Bletch) != 0) {\r
+                       /* prevent overflows */\r
+                       j &= Bletch - 1;\r
+                       d /= bigtens[n_bigtens-1];\r
+                       ieps++;\r
+                   }\r
+                   for(; (j != 0); j >>= 1, i++)\r
+                       if ((j & 1) != 0) {\r
+                           ieps++;\r
+                           ds *= bigtens[i];\r
+                       }\r
+                   d /= ds;\r
+               }\r
+               else if ((j1 = -k) != 0) {\r
+                   d *= tens[j1 & 0xf];\r
+                   for(j = j1 >> 4; (j != 0); j >>= 1, i++)\r
+                       if ((j & 1) != 0) {\r
+                           ieps++;\r
+                           d *= bigtens[i];\r
+                       }\r
+               }\r
+               /* Check that k was computed correctly. */\r
+               if (k_check && d < 1.0 && ilim > 0) {\r
+                               if (ilim1 <= 0)\r
+                       fast_failed = true;\r
+                               else {\r
+                                       ilim = ilim1;\r
+                                       k--;\r
+                                       d *= 10.;\r
+                                       ieps++;\r
+                               }\r
+               }\r
+               /* eps bounds the cumulative error. */\r
+//             eps = ieps*d + 7.0;\r
+//                     word0(eps) -= (P-1)*Exp_msk1;\r
+               eps = ieps*d + 7.0;\r
+               eps = setWord0(eps, word0(eps) - (P-1)*Exp_msk1);\r
+               if (ilim == 0) {\r
+                   S = mhi = null;\r
+                   d -= 5.0;\r
+                               if (d > eps) {\r
+                                       buf.append('1');\r
+                                       k++;\r
+                                       return k + 1;\r
+                               }\r
+                               if (d < -eps) {\r
+                                       buf.setLength(0);\r
+                                       buf.append('0');                /* copy "0" to buffer */\r
+                                       return 1;\r
+                               }\r
+                   fast_failed = true;\r
+               }\r
+                       if (!fast_failed) {\r
+                               fast_failed = true;\r
+                               if (leftright) {\r
+                                   /* Use Steele & White method of only\r
+                                    * generating digits needed.\r
+                                    */\r
+                                   eps = 0.5/tens[ilim-1] - eps;\r
+                                   for(i = 0;;) {\r
+                                       L = (long)d;\r
+                                       d -= L;\r
+                                       buf.append((char)('0' + L));\r
+                                               if (d < eps) {\r
+                                                       return k + 1;\r
+                                       }\r
+                                               if (1.0 - d < eps) {\r
+//                                         goto bump_up;\r
+                                                               char lastCh;\r
+                                                               while (true) {\r
+                                                                       lastCh = buf.charAt(buf.length() - 1);\r
+                                                                       buf.setLength(buf.length() - 1);\r
+                                                                       if (lastCh != '9') break;\r
+                                                                       if (buf.length() == 0) {\r
+                                                                               k++;\r
+                                                                               lastCh = '0';\r
+                                                                               break;\r
+                                                                       }\r
+                                                               }                                                               \r
+                                                               buf.append((char)(lastCh + 1));\r
+                                                               return k + 1;\r
+                                               }\r
+                                       if (++i >= ilim)\r
+                                           break;\r
+                                       eps *= 10.0;\r
+                                       d *= 10.0;\r
+                                   }\r
+                               }\r
+                               else {\r
+                                   /* Generate ilim digits, then fix them up. */\r
+                                   eps *= tens[ilim-1];\r
+                                   for(i = 1;; i++, d *= 10.0) {\r
+                                       L = (long)d;\r
+                                       d -= L;\r
+                                       buf.append((char)('0' + L));\r
+                                       if (i == ilim) {\r
+                                                       if (d > 0.5 + eps) {\r
+//                                             goto bump_up;\r
+                                                               char lastCh;\r
+                                                               while (true) {\r
+                                                                       lastCh = buf.charAt(buf.length() - 1);\r
+                                                                       buf.setLength(buf.length() - 1);\r
+                                                                       if (lastCh != '9') break;\r
+                                                                       if (buf.length() == 0) {\r
+                                                                               k++;\r
+                                                                               lastCh = '0';\r
+                                                                               break;\r
+                                                                       }\r
+                                                               }                                                               \r
+                                                               buf.append((char)(lastCh + 1));\r
+                                                               return k + 1;\r
+                                                       }\r
+                                                       else\r
+                                                               if (d < 0.5 - eps) {\r
+                                                                       while (buf.charAt(buf.length() - 1) == '0')\r
+                                                                               buf.setLength(buf.length() - 1);\r
+//                                                                     while(*--s == '0') ;\r
+//                                                                     s++;\r
+                                                                       return k + 1;\r
+                                                               }\r
+                                           break;\r
+                                       }\r
+                                   }\r
+                               }\r
+                       }\r
+                       if (fast_failed) {\r
+                               buf.setLength(0);\r
+                               d = d2;\r
+                               k = k0;\r
+                               ilim = ilim0;\r
+                       }\r
+               }\r
+\r
+           /* Do we have a "small" integer? */\r
+\r
+           if (be[0] >= 0 && k <= Int_max) {\r
+               /* Yes. */\r
+               ds = tens[k];\r
+               if (ndigits < 0 && ilim <= 0) {\r
+                   S = mhi = null;\r
+                               if (ilim < 0 || d < 5*ds || (!biasUp && d == 5*ds)) {\r
+                                       buf.setLength(0);\r
+                                       buf.append('0');                /* copy "0" to buffer */\r
+                                       return 1;\r
+                               }\r
+                               buf.append('1');\r
+                               k++;\r
+                               return k + 1;\r
+               }\r
+               for(i = 1;; i++) {\r
+                   L = (long) (d / ds);\r
+                   d -= L*ds;\r
+                   buf.append((char)('0' + L));\r
+                   if (i == ilim) {\r
+                       d += d;\r
+                       if ((d > ds) || (d == ds && (((L & 1) != 0) || biasUp))) {\r
+//                     bump_up:\r
+//                         while(*--s == '9')\r
+//                             if (s == buf) {\r
+//                                 k++;\r
+//                                 *s = '0';\r
+//                                 break;\r
+//                             }\r
+//                         ++*s++;\r
+                                               char lastCh;\r
+                                               while (true) {\r
+                                                       lastCh = buf.charAt(buf.length() - 1);\r
+                                                       buf.setLength(buf.length() - 1);\r
+                                                       if (lastCh != '9') break;\r
+                                                       if (buf.length() == 0) {\r
+                                                               k++;\r
+                                                               lastCh = '0';\r
+                                                               break;\r
+                                                       }\r
+                                               }                                                               \r
+                                               buf.append((char)(lastCh + 1));\r
+                       }\r
+                       break;\r
+                   }\r
+                               d *= 10.0;\r
+                   if (d == 0)\r
+                       break;\r
+               }\r
+                       return k + 1;\r
+           }\r
+\r
+           m2 = b2;\r
+           m5 = b5;\r
+           mhi = mlo = null;\r
+           if (leftright) {\r
+               if (mode < 2) {\r
+                   i = (denorm) ? be[0] + (Bias + (P-1) - 1 + 1) : 1 + P - bbits[0];\r
+                   /* i is 1 plus the number of trailing zero bits in d's significand. Thus,\r
+                      (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 lsb of d)/10^k. */\r
+               }\r
+               else {\r
+                   j = ilim - 1;\r
+                   if (m5 >= j)\r
+                       m5 -= j;\r
+                   else {\r
+                       s5 += j -= m5;\r
+                       b5 += j;\r
+                       m5 = 0;\r
+                   }\r
+                   if ((i = ilim) < 0) {\r
+                       m2 -= i;\r
+                       i = 0;\r
+                   }\r
+                   /* (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 * 10^(1-ilim))/10^k. */\r
+               }\r
+               b2 += i;\r
+               s2 += i;\r
+            mhi = BigInteger.valueOf(1);\r
+               /* (mhi * 2^m2 * 5^m5) / (2^s2 * 5^s5) = one-half of last printed (when mode >= 2) or\r
+                  input (when mode < 2) significant digit, divided by 10^k. */\r
+           }\r
+           /* We still have d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5).  Reduce common factors in\r
+              b2, m2, and s2 without changing the equalities. */\r
+           if (m2 > 0 && s2 > 0) {\r
+               i = (m2 < s2) ? m2 : s2;\r
+               b2 -= i;\r
+               m2 -= i;\r
+               s2 -= i;\r
+           }\r
+\r
+           /* Fold b5 into b and m5 into mhi. */\r
+           if (b5 > 0) {\r
+               if (leftright) {\r
+                   if (m5 > 0) {\r
+                       mhi = pow5mult(mhi, m5);\r
+                       b1 = mhi.multiply(b);\r
+                       b = b1;\r
+                   }\r
+                   if ((j = b5 - m5) != 0)\r
+                       b = pow5mult(b, j);\r
+               }\r
+               else\r
+                   b = pow5mult(b, b5);\r
+           }\r
+           /* Now we have d/10^k = (b * 2^b2) / (2^s2 * 5^s5) and\r
+              (mhi * 2^m2) / (2^s2 * 5^s5) = one-half of last printed or input significant digit, divided by 10^k. */\r
+\r
+           S = BigInteger.valueOf(1);\r
+           if (s5 > 0)\r
+               S = pow5mult(S, s5);\r
+           /* Now we have d/10^k = (b * 2^b2) / (S * 2^s2) and\r
+              (mhi * 2^m2) / (S * 2^s2) = one-half of last printed or input significant digit, divided by 10^k. */\r
+\r
+           /* Check for special case that d is a normalized power of 2. */\r
+           spec_case = false;\r
+           if (mode < 2) {\r
+               if ( (word1(d) == 0) && ((word0(d) & Bndry_mask) == 0)\r
+                   && ((word0(d) & (Exp_mask & Exp_mask << 1)) != 0)\r
+                   ) {\r
+                   /* The special case.  Here we want to be within a quarter of the last input\r
+                      significant digit instead of one half of it when the decimal output string's value is less than d.  */\r
+                   b2 += Log2P;\r
+                   s2 += Log2P;\r
+                   spec_case = true;\r
+               }\r
+           }\r
+\r
+           /* Arrange for convenient computation of quotients:\r
+            * shift left if necessary so divisor has 4 leading 0 bits.\r
+            *\r
+            * Perhaps we should just compute leading 28 bits of S once\r
+            * and for all and pass them and a shift to quorem, so it\r
+            * can do shifts and ors to compute the numerator for q.\r
+            */\r
+               byte [] S_bytes = S.toByteArray();\r
+               int S_hiWord = 0;\r
+               for (int idx = 0; idx < 4; idx++) {\r
+                       S_hiWord = (S_hiWord << 8);\r
+                       if (idx < S_bytes.length)\r
+                               S_hiWord |= (S_bytes[idx] & 0xFF);\r
+               }       \r
+           if ((i = (((s5 != 0) ? 32 - hi0bits(S_hiWord) : 1) + s2) & 0x1f) != 0)\r
+               i = 32 - i;\r
+           /* i is the number of leading zero bits in the most significant word of S*2^s2. */\r
+           if (i > 4) {\r
+               i -= 4;\r
+               b2 += i;\r
+               m2 += i;\r
+               s2 += i;\r
+           }\r
+           else if (i < 4) {\r
+               i += 28;\r
+               b2 += i;\r
+               m2 += i;\r
+               s2 += i;\r
+           }\r
+           /* Now S*2^s2 has exactly four leading zero bits in its most significant word. */\r
+           if (b2 > 0)\r
+               b = b.shiftLeft(b2);\r
+           if (s2 > 0)\r
+               S = S.shiftLeft(s2);\r
+           /* Now we have d/10^k = b/S and\r
+              (mhi * 2^m2) / S = maximum acceptable error, divided by 10^k. */\r
+           if (k_check) {\r
+               if (b.compareTo(S) < 0) {\r
+                   k--;\r
+                   b = b.multiply(BigInteger.valueOf(10));  /* we botched the k estimate */\r
+                   if (leftright)\r
+                       mhi = mhi.multiply(BigInteger.valueOf(10));\r
+                   ilim = ilim1;\r
+               }\r
+           }\r
+           /* At this point 1 <= d/10^k = b/S < 10. */\r
+\r
+           if (ilim <= 0 && mode > 2) {\r
+               /* We're doing fixed-mode output and d is less than the minimum nonzero output in this mode.\r
+                  Output either zero or the minimum nonzero output depending on which is closer to d. */\r
+               if ((ilim < 0 )\r
+                                       || ((i = b.compareTo(S = S.multiply(BigInteger.valueOf(5)))) < 0)\r
+                                       || ((i == 0 && !biasUp))) {\r
+               /* Always emit at least one digit.  If the number appears to be zero\r
+                  using the current mode, then emit one '0' digit and set decpt to 1. */\r
+               /*no_digits:\r
+                   k = -1 - ndigits;\r
+                   goto ret; */\r
+                               buf.setLength(0);\r
+                               buf.append('0');                /* copy "0" to buffer */\r
+                               return 1;\r
+//                 goto no_digits;\r
+               }\r
+//         one_digit:\r
+               buf.append('1');\r
+               k++;\r
+                       return k + 1;\r
+           }\r
+           if (leftright) {\r
+               if (m2 > 0)\r
+                   mhi = mhi.shiftLeft(m2);\r
+\r
+               /* Compute mlo -- check for special case\r
+                * that d is a normalized power of 2.\r
+                */\r
+\r
+               mlo = mhi;\r
+               if (spec_case) {\r
+                   mhi = mlo;\r
+                   mhi = mhi.shiftLeft(Log2P);\r
+               }\r
+               /* mlo/S = maximum acceptable error, divided by 10^k, if the output is less than d. */\r
+               /* mhi/S = maximum acceptable error, divided by 10^k, if the output is greater than d. */\r
+\r
+               for(i = 1;;i++) {                               \r
+                               BigInteger[] divResult = b.divideAndRemainder(S);\r
+                               b = divResult[1];                               \r
+                   dig = (char)(divResult[0].intValue() + '0');\r
+                   /* Do we yet have the shortest decimal string\r
+                    * that will round to d?\r
+                    */\r
+                   j = b.compareTo(mlo);\r
+                   /* j is b/S compared with mlo/S. */\r
+                   delta = S.subtract(mhi);\r
+                   j1 = (delta.signum() <= 0) ? 1 : b.compareTo(delta);\r
+                   /* j1 is b/S compared with 1 - mhi/S. */\r
+                   if ((j1 == 0) && (mode == 0) && ((word1(d) & 1) == 0)) {\r
+                                       if (dig == '9') {\r
+                                               buf.append('9');\r
+                                               if (roundOff(buf)) {\r
+                                                       k++;\r
+                                                       buf.append('1');\r
+                                               }\r
+                                               return k + 1;\r
+//                         goto round_9_up;\r
+                                       }\r
+                       if (j > 0)\r
+                           dig++;\r
+                       buf.append(dig);\r
+                                       return k + 1;\r
+                   }\r
+                   if ((j < 0) \r
+                                               || ((j == 0) \r
+                                                       && (mode == 0)\r
+                                                       && ((word1(d) & 1) == 0)\r
+                       )) {\r
+                       if (j1 > 0) {\r
+                           /* Either dig or dig+1 would work here as the least significant decimal digit.\r
+                              Use whichever would produce a decimal value closer to d. */\r
+                           b = b.shiftLeft(1);\r
+                           j1 = b.compareTo(S);\r
+                           if (((j1 > 0) || (j1 == 0 && (((dig & 1) == 1) || biasUp)))\r
+                                                       && (dig++ == '9')) {\r
+                                                               buf.append('9');\r
+                                                               if (roundOff(buf)) {\r
+                                                                       k++;\r
+                                                                       buf.append('1');\r
+                                                               }\r
+                                                               return k + 1;\r
+//                                         goto round_9_up;\r
+                                               }\r
+                       }\r
+                       buf.append(dig);\r
+                                       return k + 1;\r
+                   }\r
+                   if (j1 > 0) {\r
+                       if (dig == '9') { /* possible if i == 1 */\r
+//                     round_9_up:\r
+//                         *s++ = '9';\r
+//                         goto roundoff;\r
+                                               buf.append('9');\r
+                                               if (roundOff(buf)) {\r
+                                                       k++;\r
+                                                       buf.append('1');\r
+                                               }                       \r
+                                               return k + 1;\r
+                       }\r
+                       buf.append((char)(dig + 1));\r
+                                       return k + 1;\r
+                   }\r
+                   buf.append(dig);\r
+                   if (i == ilim)\r
+                       break;\r
+                   b = b.multiply(BigInteger.valueOf(10));\r
+                   if (mlo == mhi)\r
+                       mlo = mhi = mhi.multiply(BigInteger.valueOf(10));\r
+                   else {\r
+                       mlo = mlo.multiply(BigInteger.valueOf(10));\r
+                       mhi = mhi.multiply(BigInteger.valueOf(10));\r
+                   }\r
+               }\r
+           }\r
+           else\r
+               for(i = 1;; i++) {\r
+//                 (char)(dig = quorem(b,S) + '0');\r
+                               BigInteger[] divResult = b.divideAndRemainder(S);\r
+                               b = divResult[1];                               \r
+                   dig = (char)(divResult[0].intValue() + '0');\r
+                           buf.append(dig);\r
+                   if (i >= ilim)\r
+                       break;\r
+                   b = b.multiply(BigInteger.valueOf(10));\r
+               }\r
+\r
+           /* Round off last digit */\r
+\r
+           b = b.shiftLeft(1);\r
+           j = b.compareTo(S);\r
+           if ((j > 0) || (j == 0 && (((dig & 1) == 1) || biasUp))) {\r
+//         roundoff:\r
+//             while(*--s == '9')\r
+//                 if (s == buf) {\r
+//                     k++;\r
+//                     *s++ = '1';\r
+//                     goto ret;\r
+//                 }\r
+//             ++*s++;\r
+                       if (roundOff(buf)) {\r
+                               k++;\r
+                               buf.append('1');\r
+                               return k + 1;\r
+                       }                       \r
+           }\r
+           else {\r
+               /* Strip trailing zeros */\r
+                       while (buf.charAt(buf.length() - 1) == '0')\r
+                               buf.setLength(buf.length() - 1);\r
+//             while(*--s == '0') ;\r
+//             s++;\r
+           }\r
+//       ret:\r
+//         Bfree(S);\r
+//         if (mhi) {\r
+//             if (mlo && mlo != mhi)\r
+//                 Bfree(mlo);\r
+//             Bfree(mhi);\r
+//         }\r
+//       ret1:\r
+//         Bfree(b);\r
+//         JS_ASSERT(s < buf + bufsize);\r
+           return k + 1;\r
+       }\r
+\r
+       /* Mapping of JSDToStrMode -> JS_dtoa mode */\r
+       private static final int dtoaModes[] = {\r
+           0,   /* DTOSTR_STANDARD */\r
+           0,   /* DTOSTR_STANDARD_EXPONENTIAL, */\r
+           3,   /* DTOSTR_FIXED, */\r
+           2,   /* DTOSTR_EXPONENTIAL, */\r
+           2};  /* DTOSTR_PRECISION */\r
+\r
+       static void\r
+       JS_dtostr(StringBuffer buffer, int mode, int precision, double d)\r
+       {\r
+           int decPt;                                                                  /* Position of decimal point relative to first digit returned by JS_dtoa */\r
+           boolean[] sign = new boolean[1];                    /* true if the sign bit was set in d */\r
+           int nDigits;                                                                /* Number of significand digits returned by JS_dtoa */\r
+\r
+//         JS_ASSERT(bufferSize >= (size_t)(mode <= DTOSTR_STANDARD_EXPONENTIAL ? DTOSTR_STANDARD_BUFFER_SIZE :\r
+//                 DTOSTR_VARIABLE_BUFFER_SIZE(precision)));\r
+\r
+           if (mode == DTOSTR_FIXED && (d >= 1e21 || d <= -1e21))\r
+               mode = DTOSTR_STANDARD; /* Change mode here rather than below because the buffer may not be large enough to hold a large integer. */\r
+\r
+           decPt = JS_dtoa(d, dtoaModes[mode], mode >= DTOSTR_FIXED, precision, sign, buffer);\r
+               nDigits = buffer.length();\r
+\r
+           /* If Infinity, -Infinity, or NaN, return the string regardless of the mode. */\r
+           if (decPt != 9999) {\r
+               boolean exponentialNotation = false;\r
+               int minNDigits = 0;         /* Minimum number of significand digits required by mode and precision */\r
+               int p;\r
+               int q;\r
+\r
+               switch (mode) {\r
+                   case DTOSTR_STANDARD:\r
+                       if (decPt < -5 || decPt > 21)\r
+                           exponentialNotation = true;\r
+                       else\r
+                           minNDigits = decPt;\r
+                       break;\r
+\r
+                   case DTOSTR_FIXED:\r
+                       if (precision >= 0)\r
+                           minNDigits = decPt + precision;\r
+                       else\r
+                           minNDigits = decPt;\r
+                       break;\r
+\r
+                   case DTOSTR_EXPONENTIAL:\r
+//                     JS_ASSERT(precision > 0);\r
+                       minNDigits = precision;\r
+                       /* Fall through */\r
+                   case DTOSTR_STANDARD_EXPONENTIAL:\r
+                       exponentialNotation = true;\r
+                       break;\r
+\r
+                   case DTOSTR_PRECISION:\r
+//                     JS_ASSERT(precision > 0);\r
+                       minNDigits = precision;\r
+                       if (decPt < -5 || decPt > precision)\r
+                           exponentialNotation = true;\r
+                       break;\r
+               }\r
+\r
+               /* If the number has fewer than minNDigits, pad it with zeros at the end */\r
+               if (nDigits < minNDigits) {\r
+                   p = minNDigits;\r
+                   nDigits = minNDigits;\r
+                   do {\r
+                                       buffer.append('0');\r
+                   } while (buffer.length() != p);\r
+               }\r
+               \r
+               if (exponentialNotation) {\r
+                   /* Insert a decimal point if more than one significand digit */\r
+                   if (nDigits != 1) {\r
+                                       buffer.insert(1, '.');\r
+                   }\r
+                               buffer.append('e');\r
+                               if ((decPt - 1) >= 0)\r
+                                       buffer.append('+');\r
+                               buffer.append(decPt - 1);\r
+//                 JS_snprintf(numEnd, bufferSize - (numEnd - buffer), "e%+d", decPt-1);\r
+               } else if (decPt != nDigits) {\r
+                   /* Some kind of a fraction in fixed notation */\r
+//                 JS_ASSERT(decPt <= nDigits);\r
+                   if (decPt > 0) {\r
+                       /* dd...dd . dd...dd */\r
+                                       buffer.insert(decPt, '.');\r
+                   } else {\r
+                       /* 0 . 00...00dd...dd */\r
+                                       for (int i = 0; i < 1 - decPt; i++)\r
+                                               buffer.insert(0, '0');\r
+                                       buffer.insert(1, '.');\r
+                   }\r
+               }\r
+           }\r
+\r
+           /* If negative and neither -0.0 nor NaN, output a leading '-'. */\r
+           if (sign[0] &&\r
+                   !(word0(d) == Sign_bit && word1(d) == 0) &&\r
+                   !((word0(d) & Exp_mask) == Exp_mask &&\r
+                     ((word1(d) != 0) || ((word0(d) & Frac_mask) != 0)))) {\r
+               buffer.insert(0, '-');\r
+           }\r
+       }\r
+\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/DebuggableEngineImpl.java b/src/org/mozilla/javascript/DebuggableEngineImpl.java
new file mode 100644 (file)
index 0000000..3896871
--- /dev/null
@@ -0,0 +1,111 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+import org.mozilla.javascript.debug.*;\r
+\r
+public class DebuggableEngineImpl implements DebuggableEngine {\r
+  \r
+    public DebuggableEngineImpl(Context cx) {\r
+        this.cx = cx;\r
+    }\r
+\r
+    /**\r
+     * Set whether the engine should break when it encounters\r
+     * the next line.\r
+     * <p>\r
+     * The engine will call the attached debugger's handleBreakpointHit\r
+     * method on the next line it executes if isLineStep is true.\r
+     * May be used from another thread to interrupt execution.\r
+     * \r
+     * @param isLineStep if true, break next line\r
+     */\r
+    public void setBreakNextLine(boolean isLineStep) {\r
+        cx.inLineStepMode = isLineStep;\r
+    }\r
+    \r
+    /**\r
+     * Return the value of the breakNextLine flag.\r
+     * @return true if the engine will break on execution of the \r
+     * next line.\r
+     */\r
+    public boolean getBreakNextLine() {\r
+        return cx.inLineStepMode;\r
+    }\r
+\r
+    /**\r
+     * Set the associated debugger.\r
+     * @param debugger the debugger to be used on callbacks from\r
+     * the engine.\r
+     */\r
+    public void setDebugger(Debugger debugger) {\r
+        cx.debugger = debugger;\r
+    }\r
+    \r
+    /**\r
+     * Return the current debugger.\r
+     * @return the debugger, or null if none is attached.\r
+     */\r
+    public Debugger getDebugger() {\r
+        return cx.debugger;\r
+    }\r
+    \r
+    /**\r
+     * Return the number of frames in current execution.\r
+     * @return the count of current frames\r
+     */\r
+    public int getFrameCount() {\r
+        return cx.frameStack == null ? 0 : cx.frameStack.size();\r
+    }\r
+    \r
+    /**\r
+     * Return a frame from the current execution.\r
+     * Frames are numbered starting from 0 for the innermost\r
+     * frame.\r
+     * @param frameNumber the number of the frame in the range\r
+     *        [0,frameCount-1]\r
+     * @return the relevant DebugFrame, or null if frameNumber is out\r
+     *         of range or the engine isn't currently saving \r
+     *         frames\r
+     */\r
+    public DebugFrame getFrame(int frameNumber) {\r
+        return (DebugFrame) cx.frameStack.elementAt(cx.frameStack.size() - frameNumber - 1);\r
+    }\r
+    \r
+    private Context cx;\r
+}\r
diff --git a/src/org/mozilla/javascript/DefaultErrorReporter.java b/src/org/mozilla/javascript/DefaultErrorReporter.java
new file mode 100644 (file)
index 0000000..10044e8
--- /dev/null
@@ -0,0 +1,65 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This is the default error reporter for JavaScript.\r
+ *\r
+ * @author Norris Boyd\r
+ */\r
+class DefaultErrorReporter implements ErrorReporter {\r
+\r
+    public void warning(String message, String sourceName, int line,\r
+                        String lineSource, int lineOffset)\r
+    {\r
+        // do nothing\r
+    }\r
+\r
+    public void error(String message, String sourceName, int line,\r
+                      String lineSource, int lineOffset)\r
+    {\r
+        //System.out.println("error at " + sourceName + ":" + line + "  -- " + message);\r
+        throw new EvaluatorException(message + " at " + sourceName + ":" + line);\r
+    }\r
+\r
+    public EvaluatorException runtimeError(String message, String sourceName,\r
+                                           int line, String lineSource, \r
+                                           int lineOffset)\r
+    {\r
+        //System.out.println("error at " + sourceName + ":" + line + "  -- " + message);\r
+        return new EvaluatorException(message + " at " + sourceName + ":" + line);\r
+    }\r
+}\r
diff --git a/src/org/mozilla/javascript/Delegator.java b/src/org/mozilla/javascript/Delegator.java
new file mode 100644 (file)
index 0000000..d3e9306
--- /dev/null
@@ -0,0 +1,250 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ * The contents of this file are subject to the Mozilla Public License\r
+ * Version 1.1 (the "License"); you may not use this file except in\r
+ * compliance with the License. You may obtain a copy of the License at\r
+ * http://www.mozilla.org/MPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS IS"\r
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing rights and limitations\r
+ * under the License.\r
+ *\r
+ * The Original Code is Delegator.java, released Sep 27, 2000.\r
+ *\r
+ * The Initial Developer of the Original Code is Matthias Radestock.\r
+ * <matthias@sorted.org>. Portions created by Matthias Radestock are\r
+ * Copyright (C) 2000 Matthias Radestock. All Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ *      Redfig Ltd (http://www.redfig.com)\r
+ *      LShift Ltd (http://www.lshift.net)\r
+ *\r
+ * Alternatively, the contents of this file may be used under the terms\r
+ * of the GNU Public License (the  "GPL License"), in which case the\r
+ * provisions of the GPL License are applicable instead of those\r
+ * above.  If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL License and not to allow others to use\r
+ * your version of this file under the MPL, indicate your decision by\r
+ * deleting  the provisions above and replace  them with the notice and\r
+ * other provisions required by the GPL License.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this file\r
+ * under either the MPL or the GPL License.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This is a helper class for implementing wrappers around Scriptable\r
+ * objects. It implements the Function interface and delegates all\r
+ * invocations to a delegee Scriptable object. The normal use of this\r
+ * class involves creating a sub-class and overriding one or more of\r
+ * the methods.\r
+ *\r
+ * A useful application is the implementation of interceptors,\r
+ * pre/post conditions, debugging.\r
+ *\r
+ * @see Function\r
+ * @see Scriptable\r
+ * @author Matthias Radestock\r
+ */\r
+\r
+public class Delegator implements Function {\r
+\r
+    protected Scriptable obj = null;\r
+\r
+    /**\r
+     * Create a Delegator prototype.\r
+     *\r
+     * This constructor should only be used for creating prototype\r
+     * objects of Delegator.\r
+     *\r
+     * @see org.mozilla.javascript.Delegator#construct\r
+     */\r
+    public Delegator() {\r
+    }\r
+\r
+    /**\r
+     * Create a new Delegator that forwards requests to a delegee\r
+     * Scriptable object.\r
+     *\r
+     * @param obj the delegee\r
+     * @see org.mozilla.javascript.Scriptable\r
+     */\r
+    public Delegator(Scriptable obj) {\r
+        this.obj = obj;\r
+    }\r
+\r
+    /**\r
+     * Retrieve the delegee.\r
+     *\r
+     * @return the delegee\r
+     */\r
+    public Scriptable getDelegee() {\r
+        return obj;\r
+    }\r
+    /**\r
+     * Set the delegee.\r
+     *\r
+     * @param obj the delegee\r
+     * @see org.mozilla.javascript.Scriptable\r
+     */\r
+    public void setDelegee(Scriptable obj) {\r
+        this.obj = obj;\r
+    }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#getClassName\r
+     */\r
+    public String getClassName() {\r
+        return obj.getClassName();\r
+    }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#get\r
+     */\r
+    public Object get(String name, Scriptable start) {\r
+        return obj.get(name,start);\r
+    }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#get\r
+     */\r
+    public Object get(int index, Scriptable start) {\r
+        return obj.get(index,start);\r
+        }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#has\r
+     */\r
+    public boolean has(String name, Scriptable start) {\r
+        return obj.has(name,start);\r
+        }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#has\r
+     */\r
+    public boolean has(int index, Scriptable start) {\r
+        return obj.has(index,start);\r
+        }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#put\r
+     */\r
+    public void put(String name, Scriptable start, Object value) {\r
+        obj.put(name,start,value);\r
+    }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#put\r
+     */\r
+    public void put(int index, Scriptable start, Object value) {\r
+        obj.put(index,start,value);\r
+    }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#delete\r
+     */\r
+    public void delete(String name) {\r
+        obj.delete(name);\r
+    }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#delete\r
+     */\r
+    public void delete(int index) {\r
+        obj.delete(index);\r
+    }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#getPrototype\r
+     */\r
+    public Scriptable getPrototype() {\r
+        return obj.getPrototype();\r
+    }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#setPrototype\r
+     */\r
+    public void setPrototype(Scriptable prototype) {\r
+        obj.setPrototype(prototype);\r
+    }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#getParentScope\r
+     */\r
+    public Scriptable getParentScope() {\r
+        return obj.getParentScope();\r
+    }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#setParentScope\r
+     */\r
+    public void setParentScope(Scriptable parent) {\r
+        obj.setParentScope(parent);\r
+    }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#getIds\r
+     */\r
+    public Object[] getIds() {\r
+        return obj.getIds();\r
+    }\r
+    /**\r
+     * Note that this method does not get forwarded to the delegee if\r
+     * the <code>hint</code> parameter is null,\r
+     * <code>ScriptRuntime.ScriptableClass</code> or\r
+     * <code>ScriptRuntime.FunctionClass</code>. Instead the object\r
+     * itself is returned.\r
+     *\r
+     * @param hint the type hint\r
+     * @return the default value\r
+     *\r
+     * @see org.mozilla.javascript.Scriptable#getDefaultValue\r
+     */\r
+    public Object getDefaultValue(Class hint) {\r
+        return (hint == null ||\r
+                hint == ScriptRuntime.ScriptableClass ||\r
+                hint == ScriptRuntime.FunctionClass) ?\r
+            this : obj.getDefaultValue(hint);\r
+    }\r
+    /**\r
+     * @see org.mozilla.javascript.Scriptable#hasInstance\r
+     */\r
+    public boolean hasInstance(Scriptable instance) {\r
+        return obj.hasInstance(instance);\r
+    }\r
+    /**\r
+     * @see org.mozilla.javascript.Function#call\r
+     */\r
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj,\r
+                       Object[] args)\r
+        throws JavaScriptException {\r
+        return ((Function)obj).call(cx,scope,thisObj,args);\r
+    }\r
+\r
+    /**\r
+     * Note that if the <code>delegee</code> is <code>null</code>,\r
+     * this method creates a new instance of the Delegator itself\r
+     * rathert than forwarding the call to the\r
+     * <code>delegee</code>. This permits the use of Delegator\r
+     * prototypes.\r
+     *\r
+     * @param cx the current Context for this thread\r
+     * @param scope an enclosing scope of the caller except\r
+     *              when the function is called from a closure.\r
+     * @param args the array of arguments\r
+     * @return the allocated object\r
+     * @exception JavaScriptException if an uncaught exception\r
+     *            occurred while executing the constructor\r
+     *\r
+     * @see org.mozilla.javascript.Function#construct\r
+     */\r
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args)\r
+        throws JavaScriptException {\r
+        if (obj == null) {\r
+            //this little trick allows us to declare prototype objects for\r
+            //Delegators\r
+            try {\r
+                Delegator n = (Delegator)this.getClass().newInstance();\r
+                n.setDelegee((Scriptable)args[0]);\r
+                return n;\r
+            }\r
+            catch (Exception e) {\r
+                e.printStackTrace();\r
+                System.exit(0);\r
+            }\r
+            return null;\r
+        }\r
+        else {\r
+            return ((Function)obj).construct(cx,scope,args);\r
+        }\r
+    }\r
+}\r
diff --git a/src/org/mozilla/javascript/EcmaError.java b/src/org/mozilla/javascript/EcmaError.java
new file mode 100644 (file)
index 0000000..46c3e2b
--- /dev/null
@@ -0,0 +1,152 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Roger Lawrence\r
+ * \r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * The class of exceptions raised by the engine as described in \r
+ * ECMA edition 3. See section 15.11.6 in particular.\r
+ */\r
+public class EcmaError extends RuntimeException {\r
+\r
+    /**\r
+     * Create an exception with the specified detail message.\r
+     *\r
+     * Errors internal to the JavaScript engine will simply throw a\r
+     * RuntimeException.\r
+     *\r
+     * @param nativeError the NativeError object constructed for this error\r
+     * @param sourceName the name of the source reponsible for the error\r
+     * @param lineNumber the line number of the source\r
+     * @param columnNumber the columnNumber of the source (may be zero if\r
+     *                     unknown)\r
+     * @param lineSource the source of the line containing the error (may be \r
+     *                   null if unknown)\r
+     */\r
+    public EcmaError(NativeError nativeError, String sourceName, \r
+                     int lineNumber, int columnNumber, String lineSource) \r
+    {\r
+        super("EcmaError");\r
+        errorObject = nativeError;\r
+        this.sourceName = sourceName;\r
+        this.lineNumber = lineNumber;\r
+        this.columnNumber = columnNumber;\r
+        this.lineSource = lineSource;\r
+    }\r
+    \r
+    /**\r
+     * Return a string representation of the error, which currently consists \r
+     * of the name of the error together with the message.\r
+     */\r
+    public String toString() {\r
+        if (sourceName != null && lineNumber > 0)\r
+            return errorObject.toString() + " (" + sourceName + \r
+               "; line " + lineNumber + ")";\r
+        else\r
+            return errorObject.toString();\r
+    }\r
+    \r
+    /**\r
+     * Gets the name of the error.\r
+     * \r
+     * ECMA edition 3 defines the following\r
+     * errors: EvalError, RangeError, ReferenceError, \r
+     * SyntaxError, TypeError, and URIError. Additional error names\r
+     * may be added in the future.\r
+     * \r
+     * See ECMA edition 3, 15.11.7.9.\r
+     * \r
+     * @return the name of the error. \r
+     */\r
+    public String getName() {\r
+        return errorObject.getName();\r
+    }\r
+    \r
+    /**\r
+     * Gets the message corresponding to the error.\r
+     * \r
+     * See ECMA edition 3, 15.11.7.10.\r
+     * \r
+     * @return an implemenation-defined string describing the error.\r
+     */\r
+    public String getMessage() {\r
+        return errorObject.getMessage();\r
+    }\r
+    \r
+    /**\r
+     * Get the name of the source containing the error, or null\r
+     * if that information is not available.\r
+     */\r
+    public String getSourceName() {\r
+        return sourceName;\r
+    }\r
+    \r
+    /**\r
+     * Returns the line number of the statement causing the error,\r
+     * or zero if not available.\r
+     */\r
+    public int getLineNumber() {\r
+        return lineNumber;\r
+    }\r
+    \r
+    /**\r
+     * Get the error object corresponding to this exception.\r
+     */\r
+    public Scriptable getErrorObject() {\r
+        return errorObject;\r
+    }\r
+    \r
+    /**\r
+     * The column number of the location of the error, or zero if unknown.\r
+     */\r
+    public int getColumnNumber() {\r
+        return columnNumber;\r
+    }\r
+    \r
+    /**\r
+     * The source of the line causing the error, or zero if unknown.\r
+     */\r
+    public String getLineSource() {\r
+        return lineSource;\r
+    }\r
+    \r
+    private NativeError errorObject;\r
+    private String sourceName;\r
+    private int lineNumber;\r
+    private int columnNumber;\r
+    private String lineSource;\r
+}\r