summaryrefslogtreecommitdiff
path: root/gnu
diff options
context:
space:
mode:
Diffstat (limited to 'gnu')
-rw-r--r--gnu/java/beans/DefaultExceptionListener.java66
-rw-r--r--gnu/java/security/der/DERValue.java29
-rw-r--r--gnu/java/security/der/DERWriter.java6
-rw-r--r--gnu/java/security/jce/sig/DSSKeyPairGeneratorSpi.java80
-rw-r--r--gnu/java/security/jce/sig/KeyPairGeneratorAdapter.java8
-rw-r--r--gnu/java/security/key/dss/DSSKeyPairGenerator.java80
-rw-r--r--gnu/java/security/key/dss/FIPS186.java20
-rw-r--r--gnu/java/security/key/rsa/RSAKeyPairGenerator.java16
-rw-r--r--gnu/java/security/sig/BaseSignature.java17
-rw-r--r--gnu/java/security/sig/rsa/EME_PKCS1_V1_5.java9
-rw-r--r--gnu/java/security/sig/rsa/RSA.java11
-rw-r--r--gnu/java/security/util/PRNG.java156
-rw-r--r--gnu/java/security/util/Prime2.java383
-rw-r--r--gnu/java/security/x509/ext/GeneralNames.java21
-rw-r--r--gnu/javax/crypto/key/BaseKeyAgreementParty.java18
-rw-r--r--gnu/javax/crypto/key/dh/GnuDHKeyPairGenerator.java16
-rw-r--r--gnu/javax/crypto/key/dh/RFC2631.java16
-rw-r--r--gnu/javax/crypto/key/srp6/SRPKeyPairGenerator.java16
-rw-r--r--gnu/javax/crypto/mac/BaseMac.java6
-rw-r--r--gnu/javax/crypto/mac/HMac.java15
-rw-r--r--gnu/javax/crypto/pad/PKCS1_V1_5.java4
-rw-r--r--gnu/javax/crypto/sasl/srp/KDF.java16
-rw-r--r--gnu/javax/crypto/sasl/srp/SRPClient.java18
-rw-r--r--gnu/javax/crypto/sasl/srp/SRPServer.java21
-rw-r--r--gnu/regexp/RE.java138
-rw-r--r--gnu/regexp/REMatch.java47
-rw-r--r--gnu/regexp/RESyntax.java8
-rw-r--r--gnu/regexp/RETokenBackRef.java5
-rw-r--r--gnu/regexp/RETokenNamedProperty.java297
-rw-r--r--gnu/regexp/RETokenOneOf.java17
-rw-r--r--gnu/regexp/RETokenRepeated.java252
-rw-r--r--gnu/xml/stream/SAXParser.java11
-rw-r--r--gnu/xml/stream/UnicodeReader.java2
-rw-r--r--gnu/xml/stream/XIncludeFilter.java13
-rw-r--r--gnu/xml/stream/XMLParser.java54
-rw-r--r--gnu/xml/transform/ApplyTemplatesNode.java2
36 files changed, 1298 insertions, 596 deletions
diff --git a/gnu/java/beans/DefaultExceptionListener.java b/gnu/java/beans/DefaultExceptionListener.java
new file mode 100644
index 000000000..42b31fae8
--- /dev/null
+++ b/gnu/java/beans/DefaultExceptionListener.java
@@ -0,0 +1,66 @@
+/* gnu.java.beans.DefaultExceptionListener
+ Copyright (C) 2004, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.java.beans;
+
+import java.beans.ExceptionListener;
+
+/** The DefaultExceptionListener is the default implementation of the
+ * {@link ExceptionListener} interface. An instance of
+ * this class is used whenever the user provided no
+ * <code>ExceptionListener</code> instance on its own.
+ *
+ * <p>The implementation just writes the exception's message
+ * to <code>System.err</code> and is used by the {@link java.beans.Encoder}
+ * and the {@link java.beans.XMLDecoder}.
+ * </p>
+ *
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ */
+public class DefaultExceptionListener implements ExceptionListener
+{
+ public final static DefaultExceptionListener INSTANCE
+ = new DefaultExceptionListener();
+
+ public void exceptionThrown(Exception e)
+ {
+ System.err.println("exception thrown: "
+ + e + " - message: "
+ + e.getMessage());
+ }
+
+}
diff --git a/gnu/java/security/der/DERValue.java b/gnu/java/security/der/DERValue.java
index 9a597d724..d98ce78ec 100644
--- a/gnu/java/security/der/DERValue.java
+++ b/gnu/java/security/der/DERValue.java
@@ -38,6 +38,8 @@ exception statement from your version. */
package gnu.java.security.der;
+import gnu.java.security.x509.Util;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -108,7 +110,9 @@ public class DERValue implements DER
}
catch (IOException ioe)
{
- encoded = new byte[0];
+ IllegalArgumentException iae = new IllegalArgumentException ();
+ iae.initCause (ioe);
+ throw iae;
}
}
return length;
@@ -138,7 +142,9 @@ public class DERValue implements DER
}
catch (IOException ioe)
{
- encoded = new byte[0];
+ IllegalArgumentException iae = new IllegalArgumentException ();
+ iae.initCause (ioe);
+ throw iae;
}
}
return (byte[]) encoded.clone();
@@ -156,7 +162,9 @@ public class DERValue implements DER
}
catch (IOException ioe)
{
- encoded = new byte[0];
+ IllegalArgumentException iae = new IllegalArgumentException ();
+ iae.initCause (ioe);
+ throw iae;
}
}
return encoded.length;
@@ -164,7 +172,18 @@ public class DERValue implements DER
public String toString()
{
- return "DERValue [ tag=" + tag + ", class=" + tagClass + ", constructed="
- + constructed + ", value=" + value + " ]";
+ String start = "DERValue ( [";
+ if (tagClass == DER.UNIVERSAL)
+ start = start + "UNIVERSAL ";
+ else if (tagClass == DER.PRIVATE)
+ start = start + "PRIVATE ";
+ else if (tagClass == DER.APPLICATION)
+ start = start + "APPLICATION ";
+ start = start + tag + "] constructed=" + constructed + ", value=";
+ if (constructed)
+ start = start + "\n" + Util.hexDump(getEncoded(), "\t");
+ else
+ start = start + value;
+ return start + " )";
}
}
diff --git a/gnu/java/security/der/DERWriter.java b/gnu/java/security/der/DERWriter.java
index 78524fc94..4298151e0 100644
--- a/gnu/java/security/der/DERWriter.java
+++ b/gnu/java/security/der/DERWriter.java
@@ -84,6 +84,12 @@ public class DERWriter implements DER
public static int write(OutputStream out, DERValue object)
throws IOException
{
+ if (DER.CONSTRUCTED_VALUE.equals (object.getValue ()))
+ {
+ out.write (object.getEncoded ());
+ return object.getLength ();
+ }
+
out.write(object.getExternalTag());
Object value = object.getValue();
if (value == null)
diff --git a/gnu/java/security/jce/sig/DSSKeyPairGeneratorSpi.java b/gnu/java/security/jce/sig/DSSKeyPairGeneratorSpi.java
index 5cb1380d2..2f4d36d51 100644
--- a/gnu/java/security/jce/sig/DSSKeyPairGeneratorSpi.java
+++ b/gnu/java/security/jce/sig/DSSKeyPairGeneratorSpi.java
@@ -42,7 +42,10 @@ import gnu.java.security.Registry;
import gnu.java.security.key.dss.DSSKeyPairGenerator;
import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
import java.security.SecureRandom;
+import java.security.interfaces.DSAKeyPairGenerator;
+import java.security.interfaces.DSAParams;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.DSAParameterSpec;
import java.util.HashMap;
@@ -55,9 +58,10 @@ import java.util.HashMap;
* a call to an <code>initialize()</code> method), the GNU Crypto provider
* uses a default <i>modulus</i> size (keysize) of 1024 bits.<p>
*
- * @version $Revision: 1.1 $
+ * @version $Revision: 1.1.2.1 $
*/
-public class DSSKeyPairGeneratorSpi extends KeyPairGeneratorAdapter
+public class DSSKeyPairGeneratorSpi extends KeyPairGeneratorAdapter implements
+ DSAKeyPairGenerator
{
// Constants and variables
@@ -79,14 +83,7 @@ public class DSSKeyPairGeneratorSpi extends KeyPairGeneratorAdapter
public void initialize(int keysize, SecureRandom random)
{
- HashMap attributes = new HashMap();
- attributes.put(DSSKeyPairGenerator.MODULUS_LENGTH, new Integer(keysize));
- if (random != null)
- {
- attributes.put(DSSKeyPairGenerator.SOURCE_OF_RANDOMNESS, random);
- }
-
- adaptee.setup(attributes);
+ this.initialize(keysize, false, random);
}
public void initialize(AlgorithmParameterSpec params, SecureRandom random)
@@ -96,9 +93,9 @@ public class DSSKeyPairGeneratorSpi extends KeyPairGeneratorAdapter
if (params != null)
{
if (!(params instanceof DSAParameterSpec))
- {
- throw new InvalidAlgorithmParameterException("params");
- }
+ throw new InvalidAlgorithmParameterException(
+ "Parameters argument is not a non-null instance, or " +
+ "sub-instance, of java.security.spec.DSAParameterSpec");
attributes.put(DSSKeyPairGenerator.DSS_PARAMETERS, params);
}
@@ -108,6 +105,61 @@ public class DSSKeyPairGeneratorSpi extends KeyPairGeneratorAdapter
attributes.put(DSSKeyPairGenerator.SOURCE_OF_RANDOMNESS, random);
}
- adaptee.setup(attributes);
+ try
+ {
+ adaptee.setup(attributes);
+ }
+ catch (IllegalArgumentException x)
+ {
+ InvalidAlgorithmParameterException y =
+ new InvalidAlgorithmParameterException();
+ y.initCause(x);
+ throw y;
+ }
+ }
+
+ // java.security.interfaces.DSAKeyPairGenerator interface implementation -----
+
+ public void initialize(DSAParams params, SecureRandom random)
+ throws InvalidParameterException
+ {
+ if (params == null || !(params instanceof DSAParameterSpec))
+ throw new InvalidParameterException(
+ "Parameters argument is either null or is not an instance, or " +
+ "sub-instance, of java.security.spec.DSAParameterSpec");
+ DSAParameterSpec spec = (DSAParameterSpec) params;
+ try
+ {
+ this.initialize((AlgorithmParameterSpec) spec, random);
+ }
+ catch (InvalidAlgorithmParameterException x)
+ {
+ InvalidParameterException y = new InvalidParameterException();
+ y.initCause(x);
+ throw y;
+ }
+ }
+
+ public void initialize(int modlen, boolean genParams, SecureRandom random)
+ throws InvalidParameterException
+ {
+ HashMap attributes = new HashMap();
+ attributes.put(DSSKeyPairGenerator.MODULUS_LENGTH, new Integer(modlen));
+ if (random != null)
+ attributes.put(DSSKeyPairGenerator.SOURCE_OF_RANDOMNESS, random);
+
+ attributes.put(DSSKeyPairGenerator.USE_DEFAULTS,
+ Boolean.valueOf(!genParams));
+ attributes.put(DSSKeyPairGenerator.STRICT_DEFAULTS, Boolean.TRUE);
+ try
+ {
+ adaptee.setup(attributes);
+ }
+ catch (IllegalArgumentException x)
+ {
+ InvalidParameterException y = new InvalidParameterException();
+ y.initCause(x);
+ throw y;
+ }
}
}
diff --git a/gnu/java/security/jce/sig/KeyPairGeneratorAdapter.java b/gnu/java/security/jce/sig/KeyPairGeneratorAdapter.java
index 21a596a5f..b1dab1de8 100644
--- a/gnu/java/security/jce/sig/KeyPairGeneratorAdapter.java
+++ b/gnu/java/security/jce/sig/KeyPairGeneratorAdapter.java
@@ -43,7 +43,7 @@ import gnu.java.security.key.KeyPairGeneratorFactory;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
-import java.security.KeyPairGeneratorSpi;
+import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
@@ -64,9 +64,9 @@ import java.security.spec.AlgorithmParameterSpec;
* Crypto provider uses a default <i>modulus</i> size (keysize) of 1024 bits for
* the DSS (Digital Signature Standard) a.k.a <i>DSA</i>.<p>
*
- * @version $Revision: 1.1 $
+ * @version $Revision: 1.1.2.1 $
*/
-abstract class KeyPairGeneratorAdapter extends KeyPairGeneratorSpi
+abstract class KeyPairGeneratorAdapter extends KeyPairGenerator
{
// Constants and variables
@@ -85,7 +85,7 @@ abstract class KeyPairGeneratorAdapter extends KeyPairGeneratorSpi
*/
protected KeyPairGeneratorAdapter(String kpgName)
{
- super();
+ super(kpgName);
this.adaptee = KeyPairGeneratorFactory.getInstance(kpgName);
}
diff --git a/gnu/java/security/key/dss/DSSKeyPairGenerator.java b/gnu/java/security/key/dss/DSSKeyPairGenerator.java
index 3f9c80625..16af5e048 100644
--- a/gnu/java/security/key/dss/DSSKeyPairGenerator.java
+++ b/gnu/java/security/key/dss/DSSKeyPairGenerator.java
@@ -41,6 +41,7 @@ package gnu.java.security.key.dss;
import gnu.java.security.Registry;
import gnu.java.security.hash.Sha160;
import gnu.java.security.key.IKeyPairGenerator;
+import gnu.java.security.util.PRNG;
import java.io.PrintWriter;
import java.math.BigInteger;
@@ -88,10 +89,55 @@ public class DSSKeyPairGenerator implements IKeyPairGenerator
/** Property name of the length (Integer) of the modulus (p) of a DSS key. */
public static final String MODULUS_LENGTH = "gnu.crypto.dss.L";
- /** Property name of the Boolean indicating wether or not to use defaults. */
+ /**
+ * Property name of the Boolean indicating wether or not to use default pre-
+ * computed values of <code>p</code>, <code>q</code> and <code>g</code> for
+ * a given modulus length. The ultimate behaviour of this generator with
+ * regard to using pre-computed parameter sets will depend on the value of
+ * this property and of the following one {@link #STRICT_DEFAULTS}:
+ *
+ * <ol>
+ * <li>If this property is {@link Boolean#FALSE} then this generator
+ * will accept being setup for generating parameters for any modulus length
+ * provided the modulus length is between <code>512</code> and
+ * <code>1024</code>, and is of the form <code>512 + 64 * n</code>. In
+ * addition, a new paramter set will always be generated; i.e. no pre-
+ * computed values are used.</li>
+ *
+ * <li>If this property is {@link Boolean#TRUE} and the value of
+ * {@link #STRICT_DEFAULTS} is also {@link Boolean#TRUE} then this generator
+ * will only accept being setup for generating parameters for modulus
+ * lengths of <code>512</code>, <code>768</code> and <code>1024</code>. Any
+ * other value, of the modulus length, even if between <code>512</code> and
+ * <code>1024</code>, and of the form <code>512 + 64 * n</code>, will cause
+ * an {@link IllegalArgumentException} to be thrown. When those modulus
+ * length (<code>512</code>, <code>768</code>, and <code>1024</code>) are
+ * specified, the paramter set is always the same.</li>
+ *
+ * <li>Finally, if this property is {@link Boolean#TRUE} and the value of
+ * {@link #STRICT_DEFAULTS} is {@link Boolean#FALSE} then this generator
+ * will behave as in point 1 above, except that it will use pre-computed
+ * values when possible; i.e. the modulus length is one of <code>512</code>,
+ * <code>768</code>, or <code>1024</code>.</li>
+ * </ol>
+ *
+ * The default value of this property is {@link Boolean#TRUE}.
+ */
public static final String USE_DEFAULTS = "gnu.crypto.dss.use.defaults";
/**
+ * Property name of the Boolean indicating wether or not to generate new
+ * parameters, even if the modulus length <i>L</i> is not one of the pre-
+ * computed defaults (value {@link Boolean#FALSE}), or throw an exception
+ * (value {@link Boolean#TRUE}) -- the exception in this case is an
+ * {@link IllegalArgumentException}. The default value for this property is
+ * {@link Boolean#FALSE}. The ultimate behaviour of this generator will
+ * depend on the values of this and {@link #USE_DEFAULTS} properties -- see
+ * {@link #USE_DEFAULTS} for more information.
+ */
+ public static final String STRICT_DEFAULTS = "gnu.crypto.dss.strict.defaults";
+
+ /**
* Property name of an optional {@link SecureRandom} instance to use. The
* default is to use a classloader singleton from {@link PRNG}.
*/
@@ -181,6 +227,9 @@ public class DSSKeyPairGenerator implements IKeyPairGenerator
private BigInteger XKEY;
+ /** Our default source of randomness. */
+ private PRNG prng = null;
+
// Constructor(s)
// -------------------------------------------------------------------------
@@ -222,6 +271,10 @@ public class DSSKeyPairGenerator implements IKeyPairGenerator
useDefaults = Boolean.TRUE;
}
+ Boolean strictDefaults = (Boolean) attributes.get(STRICT_DEFAULTS);
+ if (strictDefaults == null)
+ strictDefaults = Boolean.FALSE;
+
// are we given a set of DSA params or we shall use/generate our own?
DSAParameterSpec params = (DSAParameterSpec) attributes.get(DSS_PARAMETERS);
if (params != null)
@@ -250,9 +303,16 @@ public class DSSKeyPairGenerator implements IKeyPairGenerator
g = KEY_PARAMS_1024.getG();
break;
default:
- p = null;
- q = null;
- g = null;
+ if (strictDefaults.equals(Boolean.TRUE))
+ throw new IllegalArgumentException(
+ "Does not provide default parameters for " + L
+ + "-bit modulus length");
+ else
+ {
+ p = null;
+ q = null;
+ g = null;
+ }
}
}
else
@@ -353,8 +413,14 @@ public class DSSKeyPairGenerator implements IKeyPairGenerator
rnd.nextBytes(buffer);
}
else
- {
- new SecureRandom ().nextBytes(buffer);
- }
+ getDefaultPRNG().nextBytes(buffer);
+ }
+
+ private PRNG getDefaultPRNG()
+ {
+ if (prng == null)
+ prng = PRNG.getInstance();
+
+ return prng;
}
}
diff --git a/gnu/java/security/key/dss/FIPS186.java b/gnu/java/security/key/dss/FIPS186.java
index 796f24bd3..c796bab2c 100644
--- a/gnu/java/security/key/dss/FIPS186.java
+++ b/gnu/java/security/key/dss/FIPS186.java
@@ -39,6 +39,7 @@ exception statement from your version. */
package gnu.java.security.key.dss;
import gnu.java.security.hash.Sha160;
+import gnu.java.security.util.PRNG;
import gnu.java.security.util.Prime2;
import java.math.BigInteger;
@@ -53,7 +54,7 @@ import java.security.SecureRandom;
* Standard (DSS)</a>, Federal Information Processing Standards Publication 186.
* National Institute of Standards and Technology.
*
- * @version $Revision: 1.1 $
+ * @version $Revision: 1.1.2.1 $
*/
public class FIPS186
{
@@ -87,6 +88,9 @@ public class FIPS186
/** The optional {@link SecureRandom} instance to use. */
private SecureRandom rnd = null;
+ /** Our default source of randomness. */
+ private PRNG prng = null;
+
// Constructor(s)
// -------------------------------------------------------------------------
@@ -126,7 +130,7 @@ public class FIPS186
*
* The algorithm used to find these primes is as described in FIPS-186,
* section 2.2: GENERATION OF PRIMES. This prime generation scheme starts by
- * using the {@link gnu.crypto.hash.Sha160} and a user supplied <i>SEED</i>
+ * using the {@link Sha160} and a user supplied <i>SEED</i>
* to construct a prime, <code>q</code>, in the range 2<sup>159</sup> &lt; q
* &lt; 2<sup>160</sup>. Once this is accomplished, the same <i>SEED</i>
* value is used to construct an <code>X</code> in the range <code>2<sup>L-1
@@ -279,8 +283,14 @@ public class FIPS186
rnd.nextBytes(buffer);
}
else
- {
- new SecureRandom ().nextBytes(buffer);
- }
+ getDefaultPRNG().nextBytes(buffer);
+ }
+
+ private PRNG getDefaultPRNG()
+ {
+ if (prng == null)
+ prng = PRNG.getInstance();
+
+ return prng;
}
}
diff --git a/gnu/java/security/key/rsa/RSAKeyPairGenerator.java b/gnu/java/security/key/rsa/RSAKeyPairGenerator.java
index a9738e751..360036723 100644
--- a/gnu/java/security/key/rsa/RSAKeyPairGenerator.java
+++ b/gnu/java/security/key/rsa/RSAKeyPairGenerator.java
@@ -40,6 +40,7 @@ package gnu.java.security.key.rsa;
import gnu.java.security.Registry;
import gnu.java.security.key.IKeyPairGenerator;
+import gnu.java.security.util.PRNG;
import gnu.java.security.util.Prime2;
import java.math.BigInteger;
@@ -109,6 +110,9 @@ public class RSAKeyPairGenerator implements IKeyPairGenerator
/** The optional {@link SecureRandom} instance to use. */
private SecureRandom rnd = null;
+ /** Our default source of randomness. */
+ private PRNG prng = null;
+
// Constructor(s)
// -------------------------------------------------------------------------
@@ -229,8 +233,14 @@ public class RSAKeyPairGenerator implements IKeyPairGenerator
rnd.nextBytes(buffer);
}
else
- {
- new SecureRandom ().nextBytes(buffer);
- }
+ getDefaultPRNG().nextBytes(buffer);
+ }
+
+ private PRNG getDefaultPRNG()
+ {
+ if (prng == null)
+ prng = PRNG.getInstance();
+
+ return prng;
}
}
diff --git a/gnu/java/security/sig/BaseSignature.java b/gnu/java/security/sig/BaseSignature.java
index f82346feb..5b43f2c27 100644
--- a/gnu/java/security/sig/BaseSignature.java
+++ b/gnu/java/security/sig/BaseSignature.java
@@ -41,10 +41,10 @@ package gnu.java.security.sig;
import gnu.java.security.hash.IMessageDigest;
import gnu.java.security.prng.IRandom;
import gnu.java.security.prng.LimitReachedException;
+import gnu.java.security.util.PRNG;
import java.security.PrivateKey;
import java.security.PublicKey;
-import java.security.SecureRandom;
import java.util.Map;
import java.util.Random;
@@ -76,6 +76,9 @@ public abstract class BaseSignature implements ISignature
/** The optional {@link IRandom} instance to use. */
private IRandom irnd;
+ /** Our default source of randomness. */
+ private PRNG prng = null;
+
// Constructor(s)
// -------------------------------------------------------------------------
@@ -224,9 +227,7 @@ public abstract class BaseSignature implements ISignature
}
}
else
- {
- new SecureRandom ().nextBytes(buffer);
- }
+ getDefaultPRNG().nextBytes(buffer);
}
private void setup(Map attributes)
@@ -244,4 +245,12 @@ public abstract class BaseSignature implements ISignature
irnd = (IRandom) obj;
}
}
+
+ private PRNG getDefaultPRNG()
+ {
+ if (prng == null)
+ prng = PRNG.getInstance();
+
+ return prng;
+ }
}
diff --git a/gnu/java/security/sig/rsa/EME_PKCS1_V1_5.java b/gnu/java/security/sig/rsa/EME_PKCS1_V1_5.java
index d6bbfe56b..efe580d51 100644
--- a/gnu/java/security/sig/rsa/EME_PKCS1_V1_5.java
+++ b/gnu/java/security/sig/rsa/EME_PKCS1_V1_5.java
@@ -40,9 +40,9 @@ package gnu.java.security.sig.rsa;
import gnu.java.security.prng.IRandom;
import gnu.java.security.prng.LimitReachedException;
+import gnu.java.security.util.PRNG;
import java.io.ByteArrayOutputStream;
-import java.security.SecureRandom;
import java.security.interfaces.RSAKey;
import java.util.Random;
@@ -70,6 +70,9 @@ public class EME_PKCS1_V1_5
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ /** Our default source of randomness. */
+ private PRNG prng = PRNG.getInstance();
+
// Constructor(s)
// -------------------------------------------------------------------------
@@ -128,8 +131,7 @@ public class EME_PKCS1_V1_5
final byte[] PS = new byte[k - M.length - 3];
// FIXME. This should be configurable, somehow.
- SecureRandom rnd = new SecureRandom ();
- rnd.nextBytes(PS);
+ prng.nextBytes(PS);
int i = 0;
for (; i < PS.length; i++)
{
@@ -300,6 +302,5 @@ public class EME_PKCS1_V1_5
baos.reset();
return result;
-
}
}
diff --git a/gnu/java/security/sig/rsa/RSA.java b/gnu/java/security/sig/rsa/RSA.java
index 4ef7232f1..7d1707e19 100644
--- a/gnu/java/security/sig/rsa/RSA.java
+++ b/gnu/java/security/sig/rsa/RSA.java
@@ -39,12 +39,11 @@ exception statement from your version. */
package gnu.java.security.sig.rsa;
import gnu.java.security.Properties;
-import gnu.java.security.key.rsa.GnuRSAKey;
+import gnu.java.security.util.PRNG;
import java.math.BigInteger;
import java.security.PrivateKey;
import java.security.PublicKey;
-import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
@@ -79,6 +78,9 @@ public class RSA
private static final BigInteger ONE = BigInteger.ONE;
+ /** Our default source of randomness. */
+ private static final PRNG prng = PRNG.getInstance();
+
// Constructor(s)
// -------------------------------------------------------------------------
@@ -340,16 +342,15 @@ public class RSA
final int upper = (N.bitLength() + 7) / 8;
final int lower = upper / 2;
final byte[] bl = new byte[1];
- SecureRandom rnd = new SecureRandom ();
int b;
do
{
- rnd.nextBytes(bl);
+ prng.nextBytes(bl);
b = bl[0] & 0xFF;
}
while (b < lower || b > upper);
final byte[] buffer = new byte[b]; // 256-bit MPI
- rnd.nextBytes(buffer);
+ prng.nextBytes(buffer);
return new BigInteger(1, buffer);
}
}
diff --git a/gnu/java/security/util/PRNG.java b/gnu/java/security/util/PRNG.java
new file mode 100644
index 000000000..138cc6bcb
--- /dev/null
+++ b/gnu/java/security/util/PRNG.java
@@ -0,0 +1,156 @@
+/* PRNG.java -- A Utility methods for default source of randomness
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.security.util;
+
+import java.util.HashMap;
+
+import gnu.java.security.prng.IRandom;
+import gnu.java.security.prng.LimitReachedException;
+import gnu.java.security.prng.MDGenerator;
+
+/**
+ * A useful hash-based (SHA) pseudo-random number generator used
+ * throughout this library.
+ *
+ * @see MDGenerator
+ */
+public class PRNG
+{
+ // Constans and fields
+ // --------------------------------------------------------------------------
+
+ /** The underlying {@link IRandom}. */
+ private IRandom delegate;
+
+ // Constructor(s)
+ // --------------------------------------------------------------------------
+
+ /**
+ * Private constructor to enforce using the Factory method.
+ *
+ * @param delegate
+ * the undelying {@link IRandom} object used.
+ */
+ private PRNG(IRandom delegate)
+ {
+ super();
+
+ this.delegate = delegate;
+ }
+
+ // Class methods
+ // --------------------------------------------------------------------------
+
+ public static final PRNG getInstance()
+ {
+ IRandom delegate = new MDGenerator();
+ try
+ {
+ HashMap map = new HashMap();
+ // initialise it with a seed
+ long t = System.currentTimeMillis();
+ byte[] seed = new byte[] {
+ (byte) (t >>> 56), (byte) (t >>> 48),
+ (byte) (t >>> 40), (byte) (t >>> 32),
+ (byte) (t >>> 24), (byte) (t >>> 16),
+ (byte) (t >>> 8), (byte) t};
+ map.put(MDGenerator.SEEED, seed);
+ delegate.init(map); // default is to use SHA-1 hash
+ }
+ catch (Exception x)
+ {
+ throw new ExceptionInInitializerError(x);
+ }
+
+ return new PRNG(delegate);
+ }
+
+ // Instance methods
+ // --------------------------------------------------------------------------
+
+ /**
+ * Completely fills the designated <code>buffer</code> with random data
+ * generated by the underlying delegate.
+ *
+ * @param buffer
+ * the place holder of random bytes generated by the underlying
+ * delegate. On output, the contents of <code>buffer</code> are
+ * replaced with pseudo-random data, iff the <code>buffer</code>
+ * size is not zero.
+ */
+ public void nextBytes(byte[] buffer)
+ {
+ nextBytes(buffer, 0, buffer.length);
+ }
+
+ /**
+ * Fills the designated <code>buffer</code>, starting from byte at position
+ * <code>offset</code> with, at most, <code>length</code> bytes of random
+ * data generated by the underlying delegate.
+ *
+ * @see IRandom#nextBytes
+ */
+ public void nextBytes(byte[] buffer, int offset, int length)
+ {
+ try
+ {
+ delegate.nextBytes(buffer, offset, length);
+ }
+ catch (LimitReachedException x) // re-initialise with a seed
+ {
+ try
+ {
+ HashMap map = new HashMap();
+ long t = System.currentTimeMillis();
+ byte[] seed = new byte[] {
+ (byte)(t >>> 56), (byte)(t >>> 48),
+ (byte)(t >>> 40), (byte)(t >>> 32),
+ (byte)(t >>> 24), (byte)(t >>> 16),
+ (byte)(t >>> 8), (byte) t };
+ map.put(MDGenerator.SEEED, seed);
+ delegate.init(map); // default is to use SHA-1 hash
+ delegate.nextBytes(buffer, offset, length);
+ }
+ catch (Exception y)
+ {
+ throw new ExceptionInInitializerError(y);
+ }
+ }
+ }
+}
diff --git a/gnu/java/security/util/Prime2.java b/gnu/java/security/util/Prime2.java
index bf8df55ad..6e46f5fca 100644
--- a/gnu/java/security/util/Prime2.java
+++ b/gnu/java/security/util/Prime2.java
@@ -38,13 +38,10 @@ exception statement from your version. */
package gnu.java.security.util;
-import gnu.java.security.Properties;
-
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.math.BigInteger;
import java.util.Map;
-import java.util.Random;
import java.util.WeakHashMap;
/**
@@ -189,7 +186,7 @@ public class Prime2
/**
* <p>Java port of Colin Plumb primality test (Euler Criterion)
* implementation for a base of 2 --from bnlib-1.1 release, function
- * primeTest() in prime.c. this is his comments; (bn is our w).</p>
+ * primeTest() in prime.c. this is his comments.</p>
*
* <p>"Now, check that bn is prime. If it passes to the base 2, it's prime
* beyond all reasonable doubt, and everything else is just gravy, but it
@@ -234,86 +231,61 @@ public class Prime2
* All primes <code>== 1 (mod 4)</code> can be expressed as <code>a^2 +
* (2*b)^2</code>, but I see no cheap way to evaluate this condition."</p>
*
- * @param w the number to test.
+ * @param bn the number to test.
* @return <code>true</code> iff the designated number passes Euler criterion
* as implemented by Colin Plumb in his <i>bnlib</i> version 1.1.
*/
-
- // FIXME this method incorrectly returns false for certain primes, notably
- // 38737, 61681, 65537, 229153, and 274177.
- public static boolean passEulerCriterion(final BigInteger w)
+ public static boolean passEulerCriterion(final BigInteger bn)
{
- // first check if it's already a known prime
- WeakReference obj = (WeakReference) knownPrimes.get(w);
- if (obj != null && w.equals(obj.get()))
- {
- if (DEBUG && debuglevel > 4)
- {
- debug("found in known primes");
- }
- return true;
- }
-
- BigInteger w_minus_one = w.subtract(ONE);
- BigInteger e = w_minus_one;
+ BigInteger bn_minus_one = bn.subtract(ONE);
+ BigInteger e = bn_minus_one;
// l is the 3 least-significant bits of e
int l = e.and(BigInteger.valueOf(7L)).intValue();
int j = 1; // Where to start in prime array for strong prime tests
- BigInteger A;
+ BigInteger a;
int k;
- if ((l & 7) != 0)
+ if (l != 0)
{
e = e.shiftRight(1);
- A = TWO.modPow(e, w);
- if ((l & 7) == 6)
- { // bn == 7 mod 8, expect +1
- if (A.bitCount() != 1)
+ a = TWO.modPow(e, bn);
+ if (l == 6) // bn == 7 mod 8, expect +1
+ {
+ if (a.bitLength() != 1)
{
- if (DEBUG && debuglevel > 4)
- {
- debug(w.toString(16) + " fails Euler criterion #1...");
- }
+ debugBI("Fails Euler criterion #1", bn);
return false; // Not prime
}
k = 1;
}
- else
- { // bn == 3 or 5 mod 8, expect -1 == bn-1
- A = A.add(ONE);
- if (!A.equals(w))
+ else // bn == 3 or 5 mod 8, expect -1 == bn-1
+ {
+ a = a.add(ONE);
+ if (a.compareTo(bn) != 0)
{
- if (DEBUG && debuglevel > 4)
- {
- debug(w.toString(16) + " fails Euler criterion #2...");
- }
+ debugBI("Fails Euler criterion #2", bn);
return false; // Not prime
}
k = 1;
- if ((l & 4) != 0)
- { // bn == 5 mod 8, make odd for strong tests
+ if ((l & 4) != 0) // bn == 5 mod 8, make odd for strong tests
+ {
e = e.shiftRight(1);
k = 2;
}
}
}
- else
- { // bn == 1 mod 8, expect 2^((bn-1)/4) == +/-1 mod bn
+ else // bn == 1 mod 8, expect 2^((bn-1)/4) == +/-1 mod bn
+ {
e = e.shiftRight(2);
- A = TWO.modPow(e, w);
- if (A.bitCount() == 1)
- {
- j = 0; // Re-do strong prime test to base 2
- }
+ a = TWO.modPow(e, bn);
+ if (a.bitLength() == 1)
+ j = 0; // Re-do strong prime test to base 2
else
{
- A = A.add(ONE);
- if (!A.equals(w))
+ a = a.add(ONE);
+ if (a.compareTo(bn) != 0)
{
- if (DEBUG && debuglevel > 4)
- {
- debug(w.toString(16) + " fails Euler criterion #3...");
- }
+ debugBI("Fails Euler criterion #3", bn);
return false; // Not prime
}
}
@@ -329,187 +301,38 @@ public class Prime2
// when the previous test was as good as a strong prime test, but 1/8 of
// the time, j = 0 because the strong prime test to the base 2 needs to
// be re-done.
- //for (int i = j; i < SMALL_PRIME_COUNT; i++) {
- for (int i = j; i < 13; i++)
- { // try only the first 13 primes
- A = SMALL_PRIME[i];
- A = A.modPow(e, w);
- if (A.bitCount() == 1)
- {
- continue; // Passed this test
- }
+ for (int i = j; i < 7; i++) // try only the first 7 primes
+ {
+ a = SMALL_PRIME[i];
+ a = a.modPow(e, bn);
+ if (a.bitLength() == 1)
+ continue; // Passed this test
+
l = k;
while (true)
{
- // A = A.add(ONE);
- // if (A.equals(w)) { // Was result bn-1?
- if (A.equals(w_minus_one))
- { // Was result bn-1?
- break; // Prime
- }
- if (--l == 0)
- { // Reached end, not -1? luck?
- if (DEBUG && debuglevel > 4)
- {
- debug(w.toString(16) + " fails Euler criterion #4...");
- }
+// a = a.add(ONE);
+// if (a.compareTo(w) == 0) { // Was result bn-1?
+ if (a.compareTo(bn_minus_one) == 0) // Was result bn-1?
+ break; // Prime
+
+ if (--l == 0) // Reached end, not -1? luck?
+ {
+ debugBI("Fails Euler criterion #4", bn);
return false; // Failed, not prime
}
// This portion is executed, on average, once
- // A = A.subtract(ONE); // Put a back where it was
- A = A.modPow(TWO, w);
- if (A.bitCount() == 1)
+// a = a.subtract(ONE); // Put a back where it was
+ a = a.modPow(TWO, bn);
+ if (a.bitLength() == 1)
{
- if (DEBUG && debuglevel > 4)
- {
- debug(w.toString(16) + " fails Euler criterion #5...");
- }
+ debugBI("Fails Euler criterion #5", bn);
return false; // Failed, not prime
}
}
// It worked (to the base primes[i])
}
- if (DEBUG && debuglevel > 4)
- {
- debug(w.toString(16) + " passes Euler criterion...");
- }
-
- // store it in the known primes weak hash-map
- knownPrimes.put(w, new WeakReference(w));
-
- return true;
- }
-
- /**
- * <p>Checks Fermat's Little Theorem for base <i>b</i>; i.e.
- * <code><i>b</i>**(w-1) == 1 (mod w)</code>.</p>
- *
- * @param w the number to test.
- * @param t the number of random bases to test.
- * @return <code>true</code> iff <code><i>b</i>**(w-1) == 1 (mod w)</code>,
- * for some <i>b</i>.
- */
- public static boolean passFermatLittleTheorem(final BigInteger w, int t)
- {
- final BigInteger w_minus_one = w.subtract(ONE);
- if (t <= 0)
- {
- t = 10; // XXX
- }
- if (!TWO.modPow(w_minus_one, w).equals(ONE))
- {
- if (DEBUG && debuglevel > 4)
- {
- debug(w.toString(16) + " fails Fermat's little theorem for base 2");
- }
- return false;
- }
- for (int i = 0; i < t; i++)
- {
- byte[] buf = new byte[(w.bitLength() + 7) / 8 - 1];
- BigInteger base = null;
- do
- {
- new Random ().nextBytes(buf);
- base = new BigInteger(1, buf);
- }
- while (base.compareTo(TWO) < 0 || base.compareTo(w_minus_one) > 0);
- if (!base.modPow(w_minus_one, w).equals(ONE))
- {
- if (DEBUG && debuglevel > 4)
- {
- debug(w.toString(16)
- + " fails Fermat's little theorem for base "
- + base.toString(16));
- }
- return false;
- }
- }
- if (DEBUG && debuglevel > 4)
- {
- debug(w.toString(16) + " passes Fermat's little theorem for " + t
- + " bases...");
- }
- return true;
- }
-
- /**
- * <p>Applies the Miller-Rabin strong probabilistic primality test.</p>
- *
- * <p>The HAC (Handbook of Applied Cryptography), Alfred Menezes & al. Note
- * 4.57 states that for <code>q</code>, <code>n=18</code> is enough while
- * for <code>p</code>, <code>n=6</code> (512 bits) or <code>n=3</cdoe> (1024
- * bits) are enough to yield <i>robust</i> primality tests. The values used
- * are from table 4.4 given in Note 4.49.</p>
- *
- * @param w the number to test.
- * @return <code>true</code> iff the designated number passes the Miller-
- * Rabin probabilistic primality test for a computed number of rounds.
- */
- public static boolean passMillerRabin(BigInteger n, int t)
- {
- int nbytes = (n.bitLength() + 7) / 8;
- byte[] ab = new byte[nbytes];
-
- // 1. Write n - 1 = 2^s * r.
- BigInteger n_minus_1 = n.subtract(ONE);
- BigInteger r = n_minus_1;
- int s = 0;
- while (!r.testBit(0))
- {
- r = r.shiftRight(1);
- s++;
- }
-
- // 2. For i from 1 to t, do:
- for (int i = 0; i < t; i++)
- {
-
- // 2.1 Choose a random integer a, 2 <= a <= n - 2.
- BigInteger a;
- do
- {
- new Random ().nextBytes(ab);
- a = new BigInteger(1, ab);
- }
- while (a.compareTo(TWO) < 0 || a.compareTo(n) > 0);
-
- // 2.2 Compute y = a^r mod n.
- BigInteger y = a.modPow(r, n);
-
- // If y != 1 and y != n - 1, then:
- if (!y.equals(ONE) && !y.equals(n_minus_1))
- {
- for (int j = 1; j < s - 1 && !y.equals(n_minus_1); j++)
- {
- // Compute y = y^2 mod n.
- y = y.modPow(TWO, n);
-
- // If y = 1 return "composite"
- if (y.equals(ONE))
- {
- if (DEBUG && debuglevel > 4)
- {
- debug(n.toString(16) + " fails Miller-Rabin test...");
- }
- return false;
- }
- }
- // If y != n - 1 return "composite"
- if (!y.equals(n_minus_1))
- {
- if (DEBUG && debuglevel > 4)
- {
- debug(n.toString(16) + " fails Miller-Rabin test...");
- }
- return false;
- }
- }
- }
- if (DEBUG && debuglevel > 4)
- {
- debug(n.toString(16) + " passes Miller-Rabin test...");
- }
+ debugBI("Passes Euler criterion", bn);
return true;
}
@@ -519,114 +342,76 @@ public class Prime2
}
/**
- * <p>This implementation does not rely solely on the Miller-Rabin strong
- * probabilistic primality test to claim the primality of the designated
- * number. It instead, tries dividing the designated number by the first 1000
- * small primes, and if no divisor was found, invokes a port of Colin Plumb's
- * implementation of the Euler Criterion, then tries the Miller-Rabin test.</p>
+ * Wrapper around {@link BigInteger#isProbablePrime(int)} with few pre-checks.
*
* @param w the integer to test.
* @param certainty the certainty with which to compute the test.
- * @param doMillerRabin if <code>true</code> and the designated integer was
- * already found to be a probable prime, then also do a Miller-Rabin test.
- * @return <code>true</code> iff the designated number has no small prime
- * divisor passes the Euler criterion, and optionally a Miller-Rabin test.
*/
public static boolean isProbablePrime(BigInteger w, int certainty)
{
// Nonnumbers are not prime.
if (w == null)
- {
return false;
- }
// eliminate trivial cases when w == 0 or 1
if (w.equals(ZERO) || w.equals(ONE))
- {
return false;
- }
// Test if w is a known small prime.
for (int i = 0; i < SMALL_PRIME_COUNT; i++)
- {
if (w.equals(SMALL_PRIME[i]))
{
if (DEBUG && debuglevel > 4)
- {
debug(w.toString(16) + " is a small prime");
- }
return true;
}
- }
- // trial division with first 1000 primes
- if (hasSmallPrimeDivisor(w))
+ // Check if it's already a known prime
+ WeakReference obj = (WeakReference) knownPrimes.get(w);
+ if (obj != null && w.equals(obj.get()))
{
if (DEBUG && debuglevel > 4)
- {
- debug(w.toString(16) + " has a small prime divisor. Rejected...");
- }
- return false;
+ debug("found in known primes");
+ return true;
}
- // Do a check with Fermat's little theorem.
- // if (passFermatLittleTheorem(w, certainty)) {
- // if (DEBUG && debuglevel > 4) {
- // debug(w.toString(16)+" passes Fermat's little theorem...");
- // }
- // } else {
- // if (DEBUG && debuglevel > 4) {
- // debug(w.toString(16)+" fails Fermat's little theorem. Rejected...");
- // }
- // return false;
- // }
-
- // Euler's criterion.
- // if (passEulerCriterion(w)) {
- // if (DEBUG && debuglevel > 4) {
- // debug(w.toString(16)+" passes Euler's criterion...");
- // }
- // } else {
- // if (DEBUG && debuglevel > 4) {
- // debug(w.toString(16)+" fails Euler's criterion. Rejected...");
- // }
- // return false;
- // }
-
- // Miller-Rabin probabilistic primality test.
- if (passMillerRabin(w, certainty))
- {
- if (DEBUG && debuglevel > 4)
- {
- debug(w.toString(16) + " passes Miller-Rabin PPT...");
- }
- }
- else
+ // trial division with first 1000 primes
+ if (hasSmallPrimeDivisor(w))
{
if (DEBUG && debuglevel > 4)
- {
- debug(w.toString(16) + " fails Miller-Rabin PPT. Rejected...");
- }
+ debug(w.toString(16) + " has a small prime divisor. Rejected...");
return false;
}
- if (DEBUG && debuglevel > 4)
- {
- debug(w.toString(16) + " is probable prime. Accepted...");
- }
+// Euler's criterion.
+// if (passEulerCriterion(w)) {
+// if (DEBUG && debuglevel > 4) {
+// debug(w.toString(16)+" passes Euler's criterion...");
+// }
+// } else {
+// if (DEBUG && debuglevel > 4) {
+// debug(w.toString(16)+" fails Euler's criterion. Rejected...");
+// }
+// return false;
+// }
+//
+// if (DEBUG && debuglevel > 4)
+// {
+// debug(w.toString(16) + " is probable prime. Accepted...");
+// }
+
+ boolean result = w.isProbablePrime(certainty);
+ if (result && certainty > 0) // store it in the known primes weak hash-map
+ knownPrimes.put(w, new WeakReference(w));
+
+ return result;
+ }
- // now compare to JDK primality test
- if (DEBUG && debuglevel > 0 && !w.isProbablePrime(certainty))
- {
- System.err.println("The gnu.crypto library and the JDK disagree on "
- + "whether 0x" + w.toString(16)
- + " is a probable prime or not.");
- System.err.println("While this library claims it is, the JDK claims"
- + " the opposite.");
- System.err.println("Please contact the maintainer of this library, "
- + "and provide this message for further investigation. TIA");
- }
+ // helper methods -----------------------------------------------------------
- return true;
+ private static final void debugBI(String msg, BigInteger bn)
+ {
+ if (DEBUG && debuglevel > 4)
+ debug("*** " + msg + ": 0x" + bn.toString(16));
}
}
diff --git a/gnu/java/security/x509/ext/GeneralNames.java b/gnu/java/security/x509/ext/GeneralNames.java
index e92aedaef..dae94cd9f 100644
--- a/gnu/java/security/x509/ext/GeneralNames.java
+++ b/gnu/java/security/x509/ext/GeneralNames.java
@@ -52,6 +52,8 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import javax.security.auth.x500.X500Principal;
+
public class GeneralNames
{
@@ -81,12 +83,14 @@ public class GeneralNames
if (!nameList.isConstructed())
throw new IOException("malformed GeneralNames");
int len = 0;
+ int i = 0;
while (len < nameList.getLength())
{
DERValue name = der.read();
List namePair = new ArrayList(2);
- if (name.getTagClass() != DER.APPLICATION)
- throw new IOException("malformed GeneralName");
+ int tagClass = name.getTagClass();
+ if (tagClass != DER.CONTEXT)
+ throw new IOException("malformed GeneralName: Tag class is " + tagClass);
namePair.add(new Integer(name.getTag()));
DERValue val = null;
switch (name.getTag())
@@ -99,6 +103,15 @@ public class GeneralNames
break;
case OTHER_NAME:
+ // MUST return the encoded bytes of the OID/OctetString sequence
+ byte[] anotherName = name.getEncoded();
+ anotherName[0] = (byte) (DER.CONSTRUCTED|DER.SEQUENCE);
+ namePair.add(anotherName);
+ // DERReader goes back on Constructed things so we need to skip over them
+ DERValue skip = der.read(); // skip OID
+ skip = der.read(); // skip Octet String
+ break;
+
case EDI_PARTY_NAME:
namePair.add(name.getValue());
break;
@@ -106,7 +119,9 @@ public class GeneralNames
case DIRECTORY_NAME:
byte[] b = name.getEncoded();
b[0] = (byte) (DER.CONSTRUCTED|DER.SEQUENCE);
- namePair.add(new X500DistinguishedName(b).toString());
+ DERReader r = new DERReader (b);
+ r.read ();
+ namePair.add(new X500Principal(r.read ().getEncoded ()).toString());
break;
case IP_ADDRESS:
diff --git a/gnu/javax/crypto/key/BaseKeyAgreementParty.java b/gnu/javax/crypto/key/BaseKeyAgreementParty.java
index 5aba47448..bfd9378d2 100644
--- a/gnu/javax/crypto/key/BaseKeyAgreementParty.java
+++ b/gnu/javax/crypto/key/BaseKeyAgreementParty.java
@@ -40,6 +40,7 @@ package gnu.javax.crypto.key;
import gnu.java.security.prng.IRandom;
import gnu.java.security.prng.LimitReachedException;
+import gnu.java.security.util.PRNG;
import java.math.BigInteger;
import java.security.SecureRandom;
@@ -75,6 +76,9 @@ public abstract class BaseKeyAgreementParty implements IKeyAgreementParty
/** The optional {@link IRandom} instance to use. */
protected IRandom irnd = null;
+ /** Our default source of randomness. */
+ private PRNG prng = null;
+
// Constructor(s)
// -------------------------------------------------------------------------
@@ -187,12 +191,18 @@ public abstract class BaseKeyAgreementParty implements IKeyAgreementParty
catch (LimitReachedException lre)
{
irnd = null;
- new SecureRandom ().nextBytes(buffer);
+ getDefaultPRNG().nextBytes(buffer);
}
}
else
- {
- new SecureRandom ().nextBytes(buffer);
- }
+ getDefaultPRNG().nextBytes(buffer);
+ }
+
+ private PRNG getDefaultPRNG()
+ {
+ if (prng == null)
+ prng = PRNG.getInstance();
+
+ return prng;
}
} \ No newline at end of file
diff --git a/gnu/javax/crypto/key/dh/GnuDHKeyPairGenerator.java b/gnu/javax/crypto/key/dh/GnuDHKeyPairGenerator.java
index 5502fa7bf..0d493d3a6 100644
--- a/gnu/javax/crypto/key/dh/GnuDHKeyPairGenerator.java
+++ b/gnu/javax/crypto/key/dh/GnuDHKeyPairGenerator.java
@@ -41,6 +41,7 @@ package gnu.javax.crypto.key.dh;
import gnu.java.security.Registry;
import gnu.java.security.hash.Sha160;
import gnu.java.security.key.IKeyPairGenerator;
+import gnu.java.security.util.PRNG;
import java.io.PrintWriter;
import java.math.BigInteger;
@@ -133,6 +134,9 @@ public class GnuDHKeyPairGenerator implements IKeyPairGenerator
private BigInteger g;
+ /** Our default source of randomness. */
+ private PRNG prng = null;
+
// Constructor(s)
// -------------------------------------------------------------------------
@@ -247,8 +251,14 @@ public class GnuDHKeyPairGenerator implements IKeyPairGenerator
rnd.nextBytes(buffer);
}
else
- {
- new SecureRandom ().nextBytes(buffer);
- }
+ getDefaultPRNG().nextBytes(buffer);
+ }
+
+ private PRNG getDefaultPRNG()
+ {
+ if (prng == null)
+ prng = PRNG.getInstance();
+
+ return prng;
}
}
diff --git a/gnu/javax/crypto/key/dh/RFC2631.java b/gnu/javax/crypto/key/dh/RFC2631.java
index 656253de1..d6e30b4bc 100644
--- a/gnu/javax/crypto/key/dh/RFC2631.java
+++ b/gnu/javax/crypto/key/dh/RFC2631.java
@@ -39,6 +39,7 @@ exception statement from your version. */
package gnu.javax.crypto.key.dh;
import gnu.java.security.hash.Sha160;
+import gnu.java.security.util.PRNG;
import gnu.java.security.util.Prime2;
import java.math.BigInteger;
@@ -87,6 +88,9 @@ public class RFC2631
/** The optional {@link SecureRandom} instance to use. */
private SecureRandom rnd = null;
+ /** Our default source of randomness. */
+ private PRNG prng = null;
+
// Constructor(s)
// -------------------------------------------------------------------------
@@ -238,8 +242,14 @@ public class RFC2631
rnd.nextBytes(buffer);
}
else
- {
- new SecureRandom ().nextBytes(buffer);
- }
+ getDefaultPRNG().nextBytes(buffer);
+ }
+
+ private PRNG getDefaultPRNG()
+ {
+ if (prng == null)
+ prng = PRNG.getInstance();
+
+ return prng;
}
}
diff --git a/gnu/javax/crypto/key/srp6/SRPKeyPairGenerator.java b/gnu/javax/crypto/key/srp6/SRPKeyPairGenerator.java
index 3c8baaa7a..2957fc3c8 100644
--- a/gnu/javax/crypto/key/srp6/SRPKeyPairGenerator.java
+++ b/gnu/javax/crypto/key/srp6/SRPKeyPairGenerator.java
@@ -40,6 +40,7 @@ package gnu.javax.crypto.key.srp6;
import gnu.java.security.Registry;
import gnu.java.security.key.IKeyPairGenerator;
+import gnu.java.security.util.PRNG;
import gnu.java.security.util.Prime2;
import java.io.PrintWriter;
@@ -126,6 +127,9 @@ public class SRPKeyPairGenerator implements IKeyPairGenerator
/** The user's verifier MPI. */
private BigInteger v;
+ /** Our default source of randomness. */
+ private PRNG prng = null;
+
// Constructor(s)
// -------------------------------------------------------------------------
@@ -334,8 +338,14 @@ public class SRPKeyPairGenerator implements IKeyPairGenerator
rnd.nextBytes(buffer);
}
else
- {
- new SecureRandom ().nextBytes(buffer);
- }
+ getDefaultPRNG().nextBytes(buffer);
+ }
+
+ private PRNG getDefaultPRNG()
+ {
+ if (prng == null)
+ prng = PRNG.getInstance();
+
+ return prng;
}
}
diff --git a/gnu/javax/crypto/mac/BaseMac.java b/gnu/javax/crypto/mac/BaseMac.java
index e5b47a937..1b42a1644 100644
--- a/gnu/javax/crypto/mac/BaseMac.java
+++ b/gnu/javax/crypto/mac/BaseMac.java
@@ -130,7 +130,11 @@ public abstract class BaseMac implements IMac
public Object clone() throws CloneNotSupportedException
{
- return super.clone();
+ BaseMac result = (BaseMac) super.clone();
+ if (this.underlyingHash != null)
+ result.underlyingHash = (IMessageDigest) this.underlyingHash.clone();
+
+ return result;
}
// methods to be implemented by concrete subclasses ------------------------
diff --git a/gnu/javax/crypto/mac/HMac.java b/gnu/javax/crypto/mac/HMac.java
index 4e664160a..c1f97b541 100644
--- a/gnu/javax/crypto/mac/HMac.java
+++ b/gnu/javax/crypto/mac/HMac.java
@@ -132,12 +132,15 @@ public class HMac extends BaseMac implements Cloneable
// java.lang.Cloneable interface implementation ----------------------------
- public Object clone()
+ public Object clone() throws CloneNotSupportedException
{
- HMac result = new HMac((IMessageDigest) underlyingHash.clone());
- result.ipadHash = this.ipadHash;
- result.opadHash = this.opadHash;
- result.ipad = this.ipad.clone();
+ HMac result = (HMac) super.clone();
+ if (this.ipadHash != null)
+ result.ipadHash = (IMessageDigest) this.ipadHash.clone();
+ if (this.opadHash != null)
+ result.opadHash = (IMessageDigest) this.opadHash.clone();
+ if (this.ipad != null)
+ result.ipad = (byte[]) this.ipad.clone();
return result;
}
@@ -322,4 +325,4 @@ public class HMac extends BaseMac implements Cloneable
}
return valid.booleanValue();
}
-} \ No newline at end of file
+}
diff --git a/gnu/javax/crypto/pad/PKCS1_V1_5.java b/gnu/javax/crypto/pad/PKCS1_V1_5.java
index 5b4f7b850..03c3d61a3 100644
--- a/gnu/javax/crypto/pad/PKCS1_V1_5.java
+++ b/gnu/javax/crypto/pad/PKCS1_V1_5.java
@@ -40,10 +40,10 @@ package gnu.javax.crypto.pad;
import gnu.java.security.Registry;
import gnu.java.security.sig.rsa.EME_PKCS1_V1_5;
+import gnu.java.security.util.PRNG;
import gnu.java.security.util.Util;
import java.io.PrintWriter;
-import java.util.Random;
/**
* <p>A padding algorithm implementation of the EME-PKCS1-V1.5 encoding/decoding
@@ -143,7 +143,7 @@ public class PKCS1_V1_5 extends BasePad
{
final int[] mLen = new int[] { 16, 20, 32, 48, 64 };
final byte[] M = new byte[mLen[mLen.length - 1]];
- new Random ().nextBytes(M);
+ PRNG.getInstance().nextBytes(M);
final byte[] EM = new byte[1024];
byte[] p;
int bs, i, j;
diff --git a/gnu/javax/crypto/sasl/srp/KDF.java b/gnu/javax/crypto/sasl/srp/KDF.java
index 161ec78b6..0d5eeacd1 100644
--- a/gnu/javax/crypto/sasl/srp/KDF.java
+++ b/gnu/javax/crypto/sasl/srp/KDF.java
@@ -40,11 +40,11 @@ package gnu.javax.crypto.sasl.srp;
import gnu.java.security.Registry;
import gnu.java.security.prng.LimitReachedException;
+import gnu.java.security.util.PRNG;
import gnu.javax.crypto.cipher.IBlockCipher;
import gnu.javax.crypto.prng.UMacGenerator;
import java.util.HashMap;
-import java.util.Random;
/**
* <p>The SASL-SRP KDF implementation, which is also used, depending on how it
@@ -60,6 +60,11 @@ public class KDF
private static final int AES_KEY_SIZE = 16; // default key size for the AES
+ private static final byte[] buffer = new byte[1];
+
+ /** Our default source of randomness. */
+ private static final PRNG prng = PRNG.getInstance();
+
/** The shared secret K to use. */
// private byte[] keyMaterial;
/** The underlying UMAC Generator instance. */
@@ -117,11 +122,18 @@ public class KDF
else
{
keyMaterial = new byte[AES_BLOCK_SIZE];
- ndx = new Random ().nextInt (256); // XXX does this need to be secure?
+ while (ndx < 1 || ndx > 255)
+ ndx = (byte) nextByte();
}
return new KDF(keyMaterial, ndx);
}
+ private static synchronized final int nextByte()
+ {
+ prng.nextBytes(buffer);
+ return (buffer[0] & 0xFF);
+ }
+
// Instance methods
// -------------------------------------------------------------------------
diff --git a/gnu/javax/crypto/sasl/srp/SRPClient.java b/gnu/javax/crypto/sasl/srp/SRPClient.java
index 26a9e8aaf..1a1664ff7 100644
--- a/gnu/javax/crypto/sasl/srp/SRPClient.java
+++ b/gnu/javax/crypto/sasl/srp/SRPClient.java
@@ -40,6 +40,7 @@ package gnu.javax.crypto.sasl.srp;
import gnu.java.security.Registry;
import gnu.java.security.hash.MD5;
+import gnu.java.security.util.PRNG;
import gnu.java.security.util.Util;
import gnu.javax.crypto.key.IKeyAgreementParty;
@@ -65,7 +66,6 @@ import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap;
import java.util.StringTokenizer;
@@ -160,6 +160,9 @@ public class SRPClient extends ClientMechanism implements SaslClient
private IKeyAgreementParty clientHandler = KeyAgreementFactory.getPartyAInstance(Registry.SRP_SASL_KA);
+ /** Our default source of randomness. */
+ private PRNG prng = null;
+
// Constructor(s)
// -------------------------------------------------------------------------
@@ -563,7 +566,8 @@ public class SRPClient extends ClientMechanism implements SaslClient
// if session re-use generate new 16-byte nonce
if (sid.length != 0)
{
- cn = new SecureRandom ().generateSeed (16);
+ cn = new byte[16];
+ getDefaultPRNG().nextBytes(cn);
}
else
{
@@ -1091,7 +1095,7 @@ public class SRPClient extends ClientMechanism implements SaslClient
final int blockSize = cipher.defaultBlockSize();
// 3. generate random iv
cIV = new byte[blockSize];
- new SecureRandom ().nextBytes(cIV);
+ getDefaultPRNG().nextBytes(cIV);
}
srp = SRP.instance(mdName);
@@ -1196,4 +1200,12 @@ public class SRPClient extends ClientMechanism implements SaslClient
outCipher));
}
}
+
+ private PRNG getDefaultPRNG()
+ {
+ if (prng == null)
+ prng = PRNG.getInstance();
+
+ return prng;
+ }
} \ No newline at end of file
diff --git a/gnu/javax/crypto/sasl/srp/SRPServer.java b/gnu/javax/crypto/sasl/srp/SRPServer.java
index 421da6e1b..25405f106 100644
--- a/gnu/javax/crypto/sasl/srp/SRPServer.java
+++ b/gnu/javax/crypto/sasl/srp/SRPServer.java
@@ -39,6 +39,7 @@ exception statement from your version. */
package gnu.javax.crypto.sasl.srp;
import gnu.java.security.Registry;
+import gnu.java.security.util.PRNG;
import gnu.java.security.util.Util;
import gnu.javax.crypto.assembly.Direction;
@@ -61,7 +62,6 @@ import java.io.PrintWriter;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
-import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap;
import java.util.StringTokenizer;
@@ -73,7 +73,7 @@ import javax.security.sasl.SaslServer;
/**
* <p>The SASL-SRP server-side mechanism.</p>
*
- * @version $Revision: 1.1 $
+ * @version $Revision: 1.1.2.1 $
*/
public class SRPServer extends ServerMechanism implements SaslServer
{
@@ -148,6 +148,9 @@ public class SRPServer extends ServerMechanism implements SaslServer
private IKeyAgreementParty serverHandler = KeyAgreementFactory.getPartyBInstance(Registry.SRP_SASL_KA);
+ /** Our default source of randomness. */
+ private PRNG prng = null;
+
// Constructor(s)
// -------------------------------------------------------------------------
@@ -593,7 +596,7 @@ public class SRPServer extends ServerMechanism implements SaslServer
{
sn = new byte[16];
}
- new SecureRandom ().nextBytes(sn);
+ getDefaultPRNG().nextBytes(sn);
setupSecurityServices(false);
@@ -1072,9 +1075,7 @@ public class SRPServer extends ServerMechanism implements SaslServer
sIV = new byte[blockSize];
if (blockSize > 0)
- {
- new SecureRandom ().nextBytes(sIV);
- }
+ getDefaultPRNG().nextBytes(sIV);
}
private void setupSecurityServices(final boolean newSession)
@@ -1144,4 +1145,12 @@ public class SRPServer extends ServerMechanism implements SaslServer
outCipher));
}
}
+
+ private PRNG getDefaultPRNG()
+ {
+ if (prng == null)
+ prng = PRNG.getInstance();
+
+ return prng;
+ }
} \ No newline at end of file
diff --git a/gnu/regexp/RE.java b/gnu/regexp/RE.java
index ad2630e68..bda977999 100644
--- a/gnu/regexp/RE.java
+++ b/gnu/regexp/RE.java
@@ -217,6 +217,13 @@ public class RE extends REToken {
*/
public static final int REG_NO_INTERPOLATE = 128;
+ /**
+ * Execution flag.
+ * Try to match the whole input string. An implicit match-end operator
+ * is added to this regexp.
+ */
+ public static final int REG_TRY_ENTIRE_MATCH = 256;
+
/** Returns a string representing the version of the gnu.regexp package. */
public static final String version() {
return VERSION;
@@ -452,6 +459,7 @@ public class RE extends REToken {
// FIXME: asciiEsc == 0 means asciiEsc is not set. But what if
// \u0000 is used as a meaningful character?
char asciiEsc = 0;
+ NamedProperty np = null;
if (("dswDSW".indexOf(pattern[index]) != -1) && syntax.get(RESyntax.RE_CHAR_CLASS_ESC_IN_LISTS)) {
switch (pattern[index]) {
case 'D':
@@ -471,6 +479,12 @@ public class RE extends REToken {
break;
}
}
+ if (("pP".indexOf(pattern[index]) != -1) && syntax.get(RESyntax.RE_NAMED_PROPERTY)) {
+ np = getNamedProperty(pattern, index - 1, pLength);
+ if (np == null)
+ throw new REException("invalid escape sequence", REException.REG_ESCAPE, index);
+ index = index - 1 + np.len - 1;
+ }
else {
CharExpression ce = getCharExpression(pattern, index - 1, pLength, syntax);
if (ce == null)
@@ -482,6 +496,8 @@ public class RE extends REToken {
if (posixID != -1) {
options.addElement(new RETokenPOSIX(subIndex,posixID,insens,negate));
+ } else if (np != null) {
+ options.addElement(getRETokenNamedProperty(subIndex,np,insens,index));
} else if (asciiEsc != 0) {
lastChar = asciiEsc;
} else {
@@ -984,6 +1000,19 @@ public class RE extends REToken {
currentToken = new RETokenChar(subIndex,ce.ch,insens);
}
+ // NAMED PROPERTY
+ // \p{prop}, \P{prop}
+
+ else if ((unit.bk && (unit.ch == 'p') && syntax.get(RESyntax.RE_NAMED_PROPERTY)) ||
+ (unit.bk && (unit.ch == 'P') && syntax.get(RESyntax.RE_NAMED_PROPERTY))) {
+ NamedProperty np = getNamedProperty(pattern, index - 2, pLength);
+ if (np == null)
+ throw new REException("invalid escape sequence", REException.REG_ESCAPE, index);
+ index = index - 2 + np.len;
+ addToken(currentToken);
+ currentToken = getRETokenNamedProperty(subIndex,np,insens,index);
+ }
+
// NON-SPECIAL CHARACTER (or escape to make literal)
// c | \* for example
@@ -1119,6 +1148,76 @@ public class RE extends REToken {
}
/**
+ * This class represents a substring in a pattern string expressing
+ * a named property.
+ * "\pA" : Property named "A"
+ * "\p{prop}" : Property named "prop"
+ * "\PA" : Property named "A" (Negated)
+ * "\P{prop}" : Property named "prop" (Negated)
+ */
+ private static class NamedProperty {
+ /** Property name */
+ String name;
+ /** Negated or not */
+ boolean negate;
+ /** length of this expression */
+ int len;
+ }
+
+ private NamedProperty getNamedProperty(char[] input, int pos, int lim) {
+ NamedProperty np = new NamedProperty();
+ char c = input[pos];
+ if (c == '\\') {
+ if (++pos >= lim) return null;
+ c = input[pos++];
+ switch(c) {
+ case 'p':
+ np.negate = false;
+ break;
+ case 'P':
+ np.negate = true;
+ break;
+ default:
+ return null;
+ }
+ c = input[pos++];
+ if (c == '{') {
+ int p = -1;
+ for (int i = pos; i < lim; i++) {
+ if (input[i] == '}') {
+ p = i;
+ break;
+ }
+ }
+ if (p < 0) return null;
+ int len = p - pos;
+ np.name = new String(input, pos, len);
+ np.len = len + 4;
+ }
+ else {
+ np.name = new String(input, pos - 1, 1);
+ np.len = 3;
+ }
+ return np;
+ }
+ else return null;
+ }
+
+ private static RETokenNamedProperty getRETokenNamedProperty(
+ int subIndex, NamedProperty np, boolean insens, int index)
+ throws REException {
+ try {
+ return new RETokenNamedProperty(subIndex, np.name, insens, np.negate);
+ }
+ catch (REException e) {
+ REException ree;
+ ree = new REException(e.getMessage(), REException.REG_ESCAPE, index);
+ ree.initCause(e);
+ throw ree;
+ }
+ }
+
+ /**
* Checks if the regular expression matches the input in its entirety.
*
* @param input The input text.
@@ -1265,20 +1364,14 @@ public class RE extends REToken {
/* Implements abstract method REToken.match() */
boolean match(CharIndexed input, REMatch mymatch) {
- int origin = mymatch.index;
- boolean b;
if (firstToken == null) {
- b = next(input, mymatch);
- if (b) mymatch.empty = (mymatch.index == origin);
- return b;
+ return next(input, mymatch);
}
// Note the start of this subexpression
mymatch.start[subIndex] = mymatch.index;
- b = firstToken.match(input, mymatch);
- if (b) mymatch.empty = (mymatch.index == origin);
- return b;
+ return firstToken.match(input, mymatch);
}
/**
@@ -1337,23 +1430,34 @@ public class RE extends REToken {
}
REMatch getMatchImpl(CharIndexed input, int anchor, int eflags, StringBuffer buffer) {
+ boolean tryEntireMatch = ((eflags & REG_TRY_ENTIRE_MATCH) != 0);
+ RE re = (tryEntireMatch ? (RE) this.clone() : this);
+ if (tryEntireMatch) {
+ re.chain(new RETokenEnd(0, null));
+ }
// Create a new REMatch to hold results
REMatch mymatch = new REMatch(numSubs, anchor, eflags);
do {
// Optimization: check if anchor + minimumLength > length
if (minimumLength == 0 || input.charAt(minimumLength-1) != CharIndexed.OUT_OF_BOUNDS) {
- if (match(input, mymatch)) {
- // Find longest match of them all to observe leftmost longest
- REMatch longest = mymatch;
+ if (re.match(input, mymatch)) {
+ REMatch best = mymatch;
+ // We assume that the match that coms first is the best.
+ // And the following "The longer, the better" rule has
+ // been commented out. The longest is not neccesarily
+ // the best. For example, "a" out of "aaa" is the best
+ // match for /a+?/.
+ /*
+ // Find best match of them all to observe leftmost longest
while ((mymatch = mymatch.next) != null) {
- if (mymatch.index > longest.index) {
- longest = mymatch;
+ if (mymatch.index > best.index) {
+ best = mymatch;
}
}
-
- longest.end[0] = longest.index;
- longest.finish(input);
- return longest;
+ */
+ best.end[0] = best.index;
+ best.finish(input);
+ return best;
}
}
mymatch.clear(++anchor);
diff --git a/gnu/regexp/REMatch.java b/gnu/regexp/REMatch.java
index e06ae36cf..ce428d48c 100644
--- a/gnu/regexp/REMatch.java
+++ b/gnu/regexp/REMatch.java
@@ -67,7 +67,8 @@ public final class REMatch implements Serializable, Cloneable {
int[] start; // start positions (relative to offset) for each (sub)exp.
int[] end; // end positions for the same
REMatch next; // other possibility (to avoid having to use arrays)
- boolean empty; // empty string matched
+ boolean empty; // empty string matched. This flag is used only within
+ // RETokenRepeated.
public Object clone() {
try {
@@ -89,7 +90,6 @@ public final class REMatch implements Serializable, Cloneable {
index = other.index;
// need to deep clone?
next = other.next;
- empty = other.empty;
}
REMatch(int subs, int anchor, int eflags) {
@@ -126,7 +126,6 @@ public final class REMatch implements Serializable, Cloneable {
start[i] = end[i] = -1;
}
next = null; // cut off alternates
- empty = false;
}
/**
@@ -180,7 +179,9 @@ public final class REMatch implements Serializable, Cloneable {
* @param sub Index of the subexpression.
*/
public String toString(int sub) {
- if ((sub >= start.length) || (start[sub] == -1)) return "";
+ if ((sub >= start.length) || sub < 0)
+ throw new IndexOutOfBoundsException("No group " + sub);
+ if (start[sub] == -1) return null;
return (matchedText.substring(start[sub],end[sub]));
}
@@ -263,4 +264,42 @@ public final class REMatch implements Serializable, Cloneable {
if (pos < input.length()) output.append(input.charAt(pos));
return output.toString();
}
+
+ static class REMatchList {
+ REMatch head;
+ REMatch tail;
+ REMatchList() {
+ head = tail = null;
+ }
+ /* Not used now. But we may need this some day?
+ void addHead(REMatch newone) {
+ if (head == null) {
+ head = newone;
+ tail = newone;
+ while (tail.next != null) {
+ tail = tail.next;
+ }
+ }
+ else {
+ REMatch tmp = newone;
+ while (tmp.next != null) tmp = tmp.next;
+ tmp.next = head;
+ head = newone;
+ }
+ }
+ */
+ void addTail(REMatch newone) {
+ if (head == null) {
+ head = newone;
+ tail = newone;
+ }
+ else {
+ tail.next = newone;
+ }
+ while (tail.next != null) {
+ tail = tail.next;
+ }
+ }
+ }
+
}
diff --git a/gnu/regexp/RESyntax.java b/gnu/regexp/RESyntax.java
index 269179364..73f4a0aaa 100644
--- a/gnu/regexp/RESyntax.java
+++ b/gnu/regexp/RESyntax.java
@@ -222,7 +222,12 @@ public final class RESyntax implements Serializable {
*/
public static final int RE_UNICODE_CHAR = 29;
- private static final int BIT_TOTAL = 30;
+ /**
+ * Syntax bit. Allow named property (\p{P}, \P{p}), as in Perl5.
+ */
+ public static final int RE_NAMED_PROPERTY = 30;
+
+ private static final int BIT_TOTAL = 31;
/**
* Predefined syntax.
@@ -445,6 +450,7 @@ public final class RESyntax implements Serializable {
.set(RE_EMBEDDED_FLAGS) // (?imsx-imsx)
.set(RE_OCTAL_CHAR) // \0377
.set(RE_HEX_CHAR) // \x1b
+ .set(RE_NAMED_PROPERTY) // \p{prop}, \P{prop}
.makeFinal();
RE_SYNTAX_PERL5_S = new RESyntax(RE_SYNTAX_PERL5)
diff --git a/gnu/regexp/RETokenBackRef.java b/gnu/regexp/RETokenBackRef.java
index 3414ecf97..060a6cf7d 100644
--- a/gnu/regexp/RETokenBackRef.java
+++ b/gnu/regexp/RETokenBackRef.java
@@ -57,7 +57,6 @@ final class RETokenBackRef extends REToken {
b = mymatch.start[num];
e = mymatch.end[num];
if ((b==-1)||(e==-1)) return false; // this shouldn't happen, but...
- int origin = mymatch.index;
for (int i=b; i<e; i++) {
char c1 = input.charAt(mymatch.index+i-b);
char c2 = input.charAt(i);
@@ -74,9 +73,7 @@ final class RETokenBackRef extends REToken {
}
}
mymatch.index += e-b;
- boolean result = next(input, mymatch);
- if (result) mymatch.empty = (mymatch.index == origin);
- return result;
+ return next(input, mymatch);
}
void dump(StringBuffer os) {
diff --git a/gnu/regexp/RETokenNamedProperty.java b/gnu/regexp/RETokenNamedProperty.java
new file mode 100644
index 000000000..c4668d7fb
--- /dev/null
+++ b/gnu/regexp/RETokenNamedProperty.java
@@ -0,0 +1,297 @@
+/* gnu/regexp/RETokenNamedProperty.java
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.regexp;
+
+final class RETokenNamedProperty extends REToken {
+ String name;
+ boolean insens;
+ boolean negate;
+ Handler handler;
+
+ // Grouped properties
+ static final byte[] LETTER = new byte[]
+ { Character.LOWERCASE_LETTER,
+ Character.UPPERCASE_LETTER,
+ Character.TITLECASE_LETTER,
+ Character.MODIFIER_LETTER,
+ Character.OTHER_LETTER };
+
+ static final byte[] MARK = new byte[]
+ { Character.NON_SPACING_MARK,
+ Character.COMBINING_SPACING_MARK,
+ Character.ENCLOSING_MARK };
+
+ static final byte[] SEPARATOR = new byte[]
+ { Character.SPACE_SEPARATOR,
+ Character.LINE_SEPARATOR,
+ Character.PARAGRAPH_SEPARATOR };
+
+ static final byte[] SYMBOL = new byte[]
+ { Character.MATH_SYMBOL,
+ Character.CURRENCY_SYMBOL,
+ Character.MODIFIER_SYMBOL,
+ Character.OTHER_SYMBOL };
+
+ static final byte[] NUMBER = new byte[]
+ { Character.DECIMAL_DIGIT_NUMBER,
+ Character.LETTER_NUMBER,
+ Character.OTHER_NUMBER };
+
+ static final byte[] PUNCTUATION = new byte[]
+ { Character.DASH_PUNCTUATION,
+ Character.START_PUNCTUATION,
+ Character.END_PUNCTUATION,
+ Character.CONNECTOR_PUNCTUATION,
+ Character.OTHER_PUNCTUATION,
+ Character.INITIAL_QUOTE_PUNCTUATION,
+ Character.FINAL_QUOTE_PUNCTUATION};
+
+ static final byte[] OTHER = new byte[]
+ { Character.CONTROL,
+ Character.FORMAT,
+ Character.PRIVATE_USE,
+ Character.SURROGATE,
+ Character.UNASSIGNED };
+
+ RETokenNamedProperty(int subIndex, String name, boolean insens, boolean negate) throws REException {
+ super(subIndex);
+ this.name = name;
+ this.insens = insens;
+ this.negate = negate;
+ handler = getHandler(name);
+ }
+
+ int getMinimumLength() {
+ return 1;
+ }
+
+ boolean match(CharIndexed input, REMatch mymatch) {
+ char ch = input.charAt(mymatch.index);
+ if (ch == CharIndexed.OUT_OF_BOUNDS)
+ return false;
+
+ boolean retval = handler.includes(ch);
+ if (insens) {
+ retval = retval ||
+ handler.includes(Character.toUpperCase(ch)) ||
+ handler.includes(Character.toLowerCase(ch));
+ }
+
+ if (negate) retval = !retval;
+ if (retval) {
+ ++mymatch.index;
+ return next(input, mymatch);
+ }
+ else return false;
+ }
+
+ void dump(StringBuffer os) {
+ os.append("\\")
+ .append(negate ? "P" : "p")
+ .append("{" + name + "}");
+ }
+
+ private abstract static class Handler {
+ public abstract boolean includes(char c);
+ }
+
+ private Handler getHandler(String name) throws REException {
+ if (name.equals("Lower") ||
+ name.equals("Upper") ||
+ // name.equals("ASCII") ||
+ name.equals("Alpha") ||
+ name.equals("Digit") ||
+ name.equals("Alnum") ||
+ name.equals("Punct") ||
+ name.equals("Graph") ||
+ name.equals("Print") ||
+ name.equals("Blank") ||
+ name.equals("Cntrl") ||
+ name.equals("XDigit") ||
+ name.equals("Space") ) {
+ return new POSIXHandler(name);
+ }
+ if (name.startsWith("In")) {
+ try {
+ name = name.substring(2);
+ Character.UnicodeBlock block = Character.UnicodeBlock.forName(name);
+ return new UnicodeBlockHandler(block);
+ }
+ catch (IllegalArgumentException e) {
+ throw new REException("Invalid Unicode block name: " + name, REException.REG_ESCAPE, 0);
+ }
+ }
+ if (name.startsWith("Is")) {
+ name = name.substring(2);
+ }
+
+ // "grouped properties"
+ if (name.equals("L"))
+ return new UnicodeCategoriesHandler(LETTER);
+ if (name.equals("M"))
+ return new UnicodeCategoriesHandler(MARK);
+ if (name.equals("Z"))
+ return new UnicodeCategoriesHandler(SEPARATOR);
+ if (name.equals("S"))
+ return new UnicodeCategoriesHandler(SYMBOL);
+ if (name.equals("N"))
+ return new UnicodeCategoriesHandler(NUMBER);
+ if (name.equals("P"))
+ return new UnicodeCategoriesHandler(PUNCTUATION);
+ if (name.equals("C"))
+ return new UnicodeCategoriesHandler(OTHER);
+
+ if (name.equals("Mc"))
+ return new UnicodeCategoryHandler(Character.COMBINING_SPACING_MARK);
+ if (name.equals("Pc"))
+ return new UnicodeCategoryHandler(Character.CONNECTOR_PUNCTUATION);
+ if (name.equals("Cc"))
+ return new UnicodeCategoryHandler(Character.CONTROL);
+ if (name.equals("Sc"))
+ return new UnicodeCategoryHandler(Character.CURRENCY_SYMBOL);
+ if (name.equals("Pd"))
+ return new UnicodeCategoryHandler(Character.DASH_PUNCTUATION);
+ if (name.equals("Nd"))
+ return new UnicodeCategoryHandler(Character.DECIMAL_DIGIT_NUMBER);
+ if (name.equals("Me"))
+ return new UnicodeCategoryHandler(Character.ENCLOSING_MARK);
+ if (name.equals("Pe"))
+ return new UnicodeCategoryHandler(Character.END_PUNCTUATION);
+ if (name.equals("Pf"))
+ return new UnicodeCategoryHandler(Character.FINAL_QUOTE_PUNCTUATION);
+ if (name.equals("Cf"))
+ return new UnicodeCategoryHandler(Character.FORMAT);
+ if (name.equals("Pi"))
+ return new UnicodeCategoryHandler(Character.INITIAL_QUOTE_PUNCTUATION);
+ if (name.equals("Nl"))
+ return new UnicodeCategoryHandler(Character.LETTER_NUMBER);
+ if (name.equals("Zl"))
+ return new UnicodeCategoryHandler(Character.LINE_SEPARATOR);
+ if (name.equals("Ll"))
+ return new UnicodeCategoryHandler(Character.LOWERCASE_LETTER);
+ if (name.equals("Sm"))
+ return new UnicodeCategoryHandler(Character.MATH_SYMBOL);
+ if (name.equals("Lm"))
+ return new UnicodeCategoryHandler(Character.MODIFIER_LETTER);
+ if (name.equals("Sk"))
+ return new UnicodeCategoryHandler(Character.MODIFIER_SYMBOL);
+ if (name.equals("Mn"))
+ return new UnicodeCategoryHandler(Character.NON_SPACING_MARK);
+ if (name.equals("Lo"))
+ return new UnicodeCategoryHandler(Character.OTHER_LETTER);
+ if (name.equals("No"))
+ return new UnicodeCategoryHandler(Character.OTHER_NUMBER);
+ if (name.equals("Po"))
+ return new UnicodeCategoryHandler(Character.OTHER_PUNCTUATION);
+ if (name.equals("So"))
+ return new UnicodeCategoryHandler(Character.OTHER_SYMBOL);
+ if (name.equals("Zp"))
+ return new UnicodeCategoryHandler(Character.PARAGRAPH_SEPARATOR);
+ if (name.equals("Co"))
+ return new UnicodeCategoryHandler(Character.PRIVATE_USE);
+ if (name.equals("Zs"))
+ return new UnicodeCategoryHandler(Character.SPACE_SEPARATOR);
+ if (name.equals("Ps"))
+ return new UnicodeCategoryHandler(Character.START_PUNCTUATION);
+ if (name.equals("Cs"))
+ return new UnicodeCategoryHandler(Character.SURROGATE);
+ if (name.equals("Lt"))
+ return new UnicodeCategoryHandler(Character.TITLECASE_LETTER);
+ if (name.equals("Cn"))
+ return new UnicodeCategoryHandler(Character.UNASSIGNED);
+ if (name.equals("Lu"))
+ return new UnicodeCategoryHandler(Character.UPPERCASE_LETTER);
+ throw new REException("unsupported name " + name, REException.REG_ESCAPE, 0);
+ }
+
+ private static class POSIXHandler extends Handler {
+ private RETokenPOSIX retoken;
+ private REMatch mymatch = new REMatch(0,0,0);
+ private char[] chars = new char[1];
+ private CharIndexedCharArray ca = new CharIndexedCharArray(chars, 0);
+ public POSIXHandler(String name) {
+ int posixId = RETokenPOSIX.intValue(name.toLowerCase());
+ if (posixId != -1)
+ retoken = new RETokenPOSIX(0,posixId,false,false);
+ else
+ throw new RuntimeException("Unknown posix ID: " + name);
+ }
+ public boolean includes(char c) {
+ chars[0] = c;
+ mymatch.index = 0;
+ return retoken.match(ca, mymatch);
+ }
+ }
+
+ private static class UnicodeCategoryHandler extends Handler {
+ public UnicodeCategoryHandler(byte category) {
+ this.category = (int)category;
+ }
+ private int category;
+ public boolean includes(char c) {
+ return Character.getType(c) == category;
+ }
+ }
+
+ private static class UnicodeCategoriesHandler extends Handler {
+ public UnicodeCategoriesHandler(byte[] categories) {
+ this.categories = categories;
+ }
+ private byte[] categories;
+ public boolean includes(char c) {
+ int category = Character.getType(c);
+ for (int i = 0; i < categories.length; i++)
+ if (category == categories[i])
+ return true;
+ return false;
+ }
+ }
+
+ private static class UnicodeBlockHandler extends Handler {
+ public UnicodeBlockHandler(Character.UnicodeBlock block) {
+ this.block = block;
+ }
+ private Character.UnicodeBlock block;
+ public boolean includes(char c) {
+ Character.UnicodeBlock cblock = Character.UnicodeBlock.of(c);
+ return (cblock != null && cblock.equals(block));
+ }
+ }
+
+}
diff --git a/gnu/regexp/RETokenOneOf.java b/gnu/regexp/RETokenOneOf.java
index 426330443..d5902c6f0 100644
--- a/gnu/regexp/RETokenOneOf.java
+++ b/gnu/regexp/RETokenOneOf.java
@@ -94,11 +94,10 @@ final class RETokenOneOf extends REToken {
}
private boolean matchP(CharIndexed input, REMatch mymatch) {
- REMatch newMatch = null;
- REMatch last = null;
+ REMatch.REMatchList newMatch = new REMatch.REMatchList();
REToken tk;
for (int i=0; i < options.size(); i++) {
- // In ordaer that the backtracking can work,
+ // In order that the backtracking can work,
// each option must be chained to the next token.
// But the chain method has some side effect, so
// we use clones.
@@ -108,21 +107,15 @@ final class RETokenOneOf extends REToken {
tk.subIndex = this.subIndex;
REMatch tryMatch = (REMatch) mymatch.clone();
if (tk.match(input, tryMatch)) { // match was successful
- if (last == null) {
- newMatch = tryMatch;
- last = tryMatch;
- } else {
- last.next = tryMatch;
- last = tryMatch;
- }
+ newMatch.addTail(tryMatch);
} // is a match
} // try next option
- if (newMatch != null) {
+ if (newMatch.head != null) {
// set contents of mymatch equal to newMatch
// try each one that matched
- mymatch.assignFrom(newMatch);
+ mymatch.assignFrom(newMatch.head);
return true;
} else {
return false;
diff --git a/gnu/regexp/RETokenRepeated.java b/gnu/regexp/RETokenRepeated.java
index 167ca9991..100b2c066 100644
--- a/gnu/regexp/RETokenRepeated.java
+++ b/gnu/regexp/RETokenRepeated.java
@@ -84,6 +84,29 @@ final class RETokenRepeated extends REToken {
return (min * token.getMinimumLength());
}
+ boolean stopMatchingIfSatisfied = true;
+
+ private static REMatch findDoables(REToken tk,
+ CharIndexed input, REMatch mymatch) {
+
+ REMatch.REMatchList doables = new REMatch.REMatchList();
+
+ // try next repeat at all possible positions
+ for (REMatch current = mymatch;
+ current != null; current = current.next) {
+ REMatch recurrent = (REMatch) current.clone();
+ int origin = recurrent.index;
+ tk = (REToken) tk.clone();
+ tk.next = tk.uncle = null;
+ if (tk.match(input, recurrent)) {
+ if (recurrent.index == origin) recurrent.empty = true;
+ // add all items in current to doables array
+ doables.addTail(recurrent);
+ }
+ }
+ return doables.head;
+ }
+
// We do need to save every possible point, but the number of clone()
// invocations here is really a killer for performance on non-stingy
// repeat operators. I'm open to suggestions...
@@ -93,65 +116,34 @@ final class RETokenRepeated extends REToken {
// the subexpression back-reference operator allow that?
boolean match(CharIndexed input, REMatch mymatch) {
- int origin = mymatch.index;
- // number of times we've matched so far
- int numRepeats = 0;
-
// Possible positions for the next repeat to match at
REMatch newMatch = mymatch;
- REMatch last = null;
- REMatch current;
- // Add the '0-repeats' index
- // positions.elementAt(z) == position [] in input after <<z>> matches
- Vector positions = new Vector();
- positions.addElement(newMatch);
+ // {0} needs some special treatment.
+ if (alwaysEmpty) {
+ REMatch result = matchRest(input, newMatch);
+ if (result != null) {
+ mymatch.assignFrom(result);
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ // number of times we've matched so far
+ int numRepeats = 0;
- // Declare variables used in loop
REMatch doables;
- REMatch doablesLast;
- REMatch recurrent;
int lastIndex = mymatch.index;
+ boolean emptyMatchFound = false;
- do {
- // Check for stingy match for each possibility.
- if ((stingy && (numRepeats >= min)) || alwaysEmpty) {
- REMatch result = matchRest(input, newMatch);
- if (result != null) {
- mymatch.assignFrom(result);
- mymatch.empty = (mymatch.index == origin);
- return true;
- }
- else {
- // Special case of {0}. It must always match an empty string.
- if (alwaysEmpty) return false;
- }
- }
-
- doables = null;
- doablesLast = null;
+ while (numRepeats < min) {
+ doables = findDoables(token, input, newMatch);
- // try next repeat at all possible positions
- for (current = newMatch; current != null; current = current.next) {
- recurrent = (REMatch) current.clone();
- if (token.match(input, recurrent)) {
- // add all items in current to doables array
- if (doables == null) {
- doables = recurrent;
- doablesLast = recurrent;
- } else {
- // Order these from longest to shortest
- // Start by assuming longest (more repeats)
- doablesLast.next = recurrent;
- }
- // Find new doablesLast
- while (doablesLast.next != null) {
- doablesLast = doablesLast.next;
- }
- }
- }
- // if none of the possibilities worked out, break out of do/while
- if (doables == null) break;
+ // if none of the possibilities worked out,
+ // it means that minimum number of repeats could not be found.
+ if (doables == null) return false;
// reassign where the next repeat can match
newMatch = doables;
@@ -159,94 +151,92 @@ final class RETokenRepeated extends REToken {
// increment how many repeats we've successfully found
++numRepeats;
- positions.addElement(newMatch);
+ if (newMatch.empty) {
+ numRepeats = min;
+ emptyMatchFound = true;
+ break;
+ }
+ lastIndex = newMatch.index;
+ }
+
+ Vector positions = new Vector();
+
+ while (numRepeats <= max) {
+ // We want to check something like
+ // if (stingy)
+ // and neglect the further matching. But experience tells
+ // such neglection may cause incomplete matching.
+ // For example, if we neglect the seemingly unnecessay
+ // matching, /^(b+?|a){1,2}?c/ cannot match "bbc".
+ // On the other hand, if we do not stop the unnecessary
+ // matching, /(([a-c])b*?\2)*/ matches "ababbbcbc"
+ // entirely when we wan to find only "ababb".
+ // In order to make regression tests pass, we do as we did.
+ if (stopMatchingIfSatisfied && stingy) {
+ REMatch results = matchRest(input, newMatch);
+ if (results != null) {
+ mymatch.assignFrom(results);
+ return true;
+ }
+ }
+ positions.add(newMatch);
+ if (emptyMatchFound) break;
+
+ doables = findDoables(token, input, newMatch);
+ if (doables == null) break;
// doables.index == lastIndex occurs either
// (1) when an empty string was the longest
// that matched this token.
- // And this case occurs either
- // (1-1) when this token is always empty,
- // for example "()" or "(())".
- // (1-2) when this token is not always empty
- // but can match an empty string, for example,
- // "a*", "(a|)".
// or
// (2) when the same string matches this token many times.
// For example, "acbab" itself matches "a.*b" and
// its substrings "acb" and "ab" also match.
+ // In this case, we do not have to go further until
+ // numRepeats == max because the more numRepeats grows,
+ // the shorter the substring matching this token becomes.
+ // So the previous succesful match must have bee the best
+ // match. But this is not necessarily the case if stingy.
if (doables.index == lastIndex) {
if (doables.empty) {
- // Case (1): We break here, otherwise we will fall
- // into an endless loop.
- if (numRepeats < min) numRepeats = min;
- break;
- }
+ emptyMatchFound = true;
+ }
else {
- // Case (2): We cannot break here because, for example,
- // "acbacb" matches "a.*b" up to 2 times but
- // not 3 times. So we have to check numRepeats >= min.
- // But we do not have to go further until numRepeats == max
- // because the more numRepeats grows, the shorter the
- // substring matching this token becomes.
- if (numRepeats > min) {
- // This means the previous match was successful,
- // and that must be the best match. This match
- // resulted in shortening the matched substring.
- numRepeats--;
- positions.remove(positions.size() - 1);
- break;
- }
- if (numRepeats == min) break;
+ if (!stingy) break;
}
- }
- lastIndex = doables.index;
- } while (numRepeats < max);
-
- // If there aren't enough repeats, then fail
- if (numRepeats < min) return false;
-
- // We're greedy, but ease off until a true match is found
- int posIndex = positions.size();
-
+ }
+ numRepeats++;
+ newMatch = doables;
+ lastIndex = newMatch.index;
+ }
+
+ // We're greedy, but ease off until a true match is found.
// At this point we've either got too many or just the right amount.
// See if this numRepeats works with the rest of the regexp.
- REMatch allResults = null;
- REMatch allResultsLast = null;
-
- REMatch results = null;
- int indexCount = posIndex - min;
- if (indexCount <= 0) {
- // This case occurs when we exited the previous do loop before
- // numRepeats >= min because an empty string matched the token.
- // In this case, an empty string can match as many times as
- // desired.
- indexCount = 1;
- }
- while (indexCount-- > 0) {
- --posIndex;
- newMatch = (REMatch) positions.elementAt(posIndex);
- results = matchRest(input, newMatch);
- if (results != null) {
- if (allResults == null) {
- allResults = results;
- allResultsLast = results;
- } else {
- // Order these from longest to shortest
- // Start by assuming longest (more repeats)
- allResultsLast.next = results;
- }
- // Find new doablesLast
- while (allResultsLast.next != null) {
- allResultsLast = allResultsLast.next;
- }
+
+ REMatch.REMatchList allResults = new REMatch.REMatchList();
+
+ int posCount = positions.size();
+ int posIndex = (stingy ? 0 : posCount - 1);
+
+ while (posCount-- > 0) {
+ REMatch m = (REMatch) positions.elementAt(posIndex);
+ if (stingy) posIndex++; else posIndex--;
+
+ REMatch results = matchRest(input, m);
+ if (results != null) {
+ // Order these from longest to shortest
+ // Start by assuming longest (more repeats)
+ // If stingy the order is shortest to longest.
+ allResults.addTail(results);
+ }
+ else {
+ if (possessive) break;
}
- // else did not match rest of the tokens, try again on smaller sample
- // or break out when performing possessive matching
- if (possessive) break;
}
- if (allResults != null) {
- mymatch.assignFrom(allResults); // does this get all?
- mymatch.empty = (mymatch.index == origin);
+
+ if (allResults.head != null) {
+ mymatch.assignFrom(allResults.head); // does this get all?
return true;
}
// If we fall out, no matches.
@@ -255,27 +245,17 @@ final class RETokenRepeated extends REToken {
private REMatch matchRest(CharIndexed input, final REMatch newMatch) {
REMatch current, single;
- REMatch doneIndex = null;
- REMatch doneIndexLast = null;
+ REMatch.REMatchList doneIndex = new REMatch.REMatchList();
// Test all possible matches for this number of repeats
for (current = newMatch; current != null; current = current.next) {
// clone() separates a single match from the chain
single = (REMatch) current.clone();
if (next(input, single)) {
// chain results to doneIndex
- if (doneIndex == null) {
- doneIndex = single;
- doneIndexLast = single;
- } else {
- doneIndexLast.next = single;
- }
- // Find new doneIndexLast
- while (doneIndexLast.next != null) {
- doneIndexLast = doneIndexLast.next;
- }
+ doneIndex.addTail(single);
}
}
- return doneIndex;
+ return doneIndex.head;
}
void dump(StringBuffer os) {
diff --git a/gnu/xml/stream/SAXParser.java b/gnu/xml/stream/SAXParser.java
index 4c0edb975..46fe4abe2 100644
--- a/gnu/xml/stream/SAXParser.java
+++ b/gnu/xml/stream/SAXParser.java
@@ -904,7 +904,16 @@ public class SAXParser
InputSource input =
entityResolver.resolveEntity(publicId, systemId);
if (input != null)
- return input.getByteStream();
+ {
+ InputStream in = input.getByteStream();
+ if (in == null)
+ {
+ String newSystemId = input.getSystemId();
+ if (newSystemId != null && !newSystemId.equals(systemId))
+ in = XMLParser.resolve(newSystemId);
+ }
+ return in;
+ }
}
catch (SAXException e)
{
diff --git a/gnu/xml/stream/UnicodeReader.java b/gnu/xml/stream/UnicodeReader.java
index c38516c30..8a1f1b83e 100644
--- a/gnu/xml/stream/UnicodeReader.java
+++ b/gnu/xml/stream/UnicodeReader.java
@@ -45,7 +45,7 @@ import java.io.Reader;
*
* @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
*/
-class UnicodeReader
+public class UnicodeReader
{
final Reader in;
diff --git a/gnu/xml/stream/XIncludeFilter.java b/gnu/xml/stream/XIncludeFilter.java
index e151ac69d..6d12bb3b2 100644
--- a/gnu/xml/stream/XIncludeFilter.java
+++ b/gnu/xml/stream/XIncludeFilter.java
@@ -42,6 +42,7 @@ import java.io.InputStreamReader;
import java.io.IOException;
import java.io.Reader;
import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;
@@ -121,7 +122,17 @@ class XIncludeFilter
boolean expandERefs)
{
super(reader);
- this.systemId = XMLParser.absolutize(null, systemId);
+ try
+ {
+ this.systemId = XMLParser.absolutize(null, systemId);
+ }
+ catch (MalformedURLException e)
+ {
+ RuntimeException e2 = new RuntimeException("unsupported URL: " +
+ systemId);
+ e2.initCause(e);
+ throw e2;
+ }
this.namespaceAware = namespaceAware;
this.validating = validating;
this.expandERefs = expandERefs;
diff --git a/gnu/xml/stream/XMLParser.java b/gnu/xml/stream/XMLParser.java
index 3545ff32d..727dcc437 100644
--- a/gnu/xml/stream/XMLParser.java
+++ b/gnu/xml/stream/XMLParser.java
@@ -1484,7 +1484,6 @@ public class XMLParser
{
if (!externalEntities)
return;
- InputStream in = null;
String url = absolutize(input.systemId, ids.systemId);
// Check for recursion
for (Iterator i = inputStack.iterator(); i.hasNext(); )
@@ -1497,7 +1496,8 @@ public class XMLParser
}
if (name == null || "".equals(name))
report = false;
- if (in == null && url != null && resolver != null)
+ InputStream in = null;
+ if (resolver != null)
{
if (resolver instanceof XMLResolver2)
in = ((XMLResolver2) resolver).resolve(ids.publicId, url);
@@ -1535,12 +1535,13 @@ public class XMLParser
* @param base the current base URL
* @param href the (absolute or relative) URL to resolve
*/
- static String absolutize(String base, String href)
+ public static String absolutize(String base, String href)
+ throws MalformedURLException
{
if (href == null)
return null;
int ci = href.indexOf(':');
- if (ci > 1 && isLowercaseAscii(href.substring(0, ci)))
+ if (ci > 1 && isURLScheme(href.substring(0, ci)))
{
// href is absolute already
return href;
@@ -1565,40 +1566,23 @@ public class XMLParser
if (!base.endsWith("/"))
base += "/";
}
- if (href.startsWith("/"))
- {
- if (base.startsWith("file:"))
- return "file://" + href;
- int i = base.indexOf("://");
- if (i != -1)
- {
- i = base.indexOf('/', i + 3);
- if (i != -1)
- base = base.substring(0, i);
- }
- }
- else
- {
- while (href.startsWith(".."))
- {
- int i = base.lastIndexOf('/', base.length() - 2);
- if (i != -1)
- base = base.substring(0, i + 1);
- href = href.substring(2);
- if (href.startsWith("/"))
- href = href.substring(1);
- }
- }
- return base + href;
+ return new URL(new URL(base), href).toString();
}
- private static boolean isLowercaseAscii(String text)
+ /**
+ * Indicates whether the specified characters match the scheme portion of
+ * a URL.
+ * @see RFC 1738 section 2.1
+ */
+ private static boolean isURLScheme(String text)
{
int len = text.length();
for (int i = 0; i < len; i++)
{
char c = text.charAt(i);
- if (c < 97 || c > 122)
+ if (c == '+' || c == '.' || c == '-')
+ continue;
+ if (c < 65 || (c > 90 && c < 97) || c > 122)
return false;
}
return true;
@@ -1607,7 +1591,7 @@ public class XMLParser
/**
* Returns an input stream for the given URL.
*/
- private InputStream resolve(String url)
+ static InputStream resolve(String url)
throws IOException
{
try
@@ -1618,6 +1602,12 @@ public class XMLParser
{
return null;
}
+ catch (IOException e)
+ {
+ IOException e2 = new IOException("error resolving " + url);
+ e2.initCause(e);
+ throw e2;
+ }
}
/**
diff --git a/gnu/xml/transform/ApplyTemplatesNode.java b/gnu/xml/transform/ApplyTemplatesNode.java
index 86d9b7d0e..38b605a07 100644
--- a/gnu/xml/transform/ApplyTemplatesNode.java
+++ b/gnu/xml/transform/ApplyTemplatesNode.java
@@ -76,7 +76,7 @@ final class ApplyTemplatesNode
TemplateNode clone(Stylesheet stylesheet)
{
- int len = sortKeys.size();
+ int len = sortKeys != null ? sortKeys.size() : 0;
List sortKeys2 = new ArrayList(len);
for (int i = 0; i < len; i++)
sortKeys2.add(((Key) sortKeys.get(i)).clone(stylesheet));