>>24) & 0xFF), (byte) ((value>>>16) & 0xFF),
+ (byte) ((value>>>8) & 0xFF), (byte) (value & 0xFF)
+ };
+ }
+
+
+
+
+ public static String digestComment(String comment) {
+ comment = comment.replaceAll("\\s+", " ").trim();
+
+ MessageDigest digest;
+ try {
+ digest = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException("MD5 digest not supported by JRE", e);
+ }
+
+ try {
+ digest.update(comment.getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalStateException("UTF-8 encoding not supported by JRE", e);
+ }
+
+ return TextUtil.hexString(digest.digest());
+ }
+
+}
diff --git a/src/net/sf/openrocket/motor/ThrustCurveMotor.java b/src/net/sf/openrocket/motor/ThrustCurveMotor.java
index e495defeb..1ccfec20c 100644
--- a/src/net/sf/openrocket/motor/ThrustCurveMotor.java
+++ b/src/net/sf/openrocket/motor/ThrustCurveMotor.java
@@ -35,8 +35,8 @@ public class ThrustCurveMotor extends Motor {
*/
public ThrustCurveMotor(Manufacturer manufacturer, String designation, String description,
Motor.Type type, double[] delays, double diameter, double length,
- double[] time, double[] thrust, Coordinate[] cg) {
- super(manufacturer, designation, description, type, delays, diameter, length);
+ double[] time, double[] thrust, Coordinate[] cg, String digest) {
+ super(manufacturer, designation, description, type, delays, diameter, length, digest);
double max = -1;
@@ -157,5 +157,5 @@ public class ThrustCurveMotor extends Motor {
public Coordinate[] getCGPoints() {
return cg.clone();
}
-
+
}
diff --git a/src/net/sf/openrocket/rocketcomponent/BodyTube.java b/src/net/sf/openrocket/rocketcomponent/BodyTube.java
index d3368f1f1..05ee9eed9 100644
--- a/src/net/sf/openrocket/rocketcomponent/BodyTube.java
+++ b/src/net/sf/openrocket/rocketcomponent/BodyTube.java
@@ -290,11 +290,26 @@ public class BodyTube extends SymmetricComponent implements MotorMount {
@Override
public Motor getMotor(String id) {
+ if (id == null)
+ return null;
+
+ // Check whether the id is valid for the current rocket
+ RocketComponent root = this.getRoot();
+ if (!(root instanceof Rocket))
+ return null;
+ if (!((Rocket) root).isMotorConfigurationID(id))
+ return null;
+
return motors.get(id);
}
@Override
public void setMotor(String id, Motor motor) {
+ if (id == null) {
+ if (motor != null) {
+ throw new IllegalArgumentException("Cannot set non-null motor for id null");
+ }
+ }
Motor current = motors.get(id);
if ((motor == null && current == null) ||
(motor != null && motor.equals(current)))
diff --git a/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java b/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java
index d5275db88..05862f3d4 100644
--- a/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java
+++ b/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java
@@ -124,7 +124,7 @@ public class FreeformFinSet extends FinSet {
/**
* Remove the fin point with the given index. The first and last fin points
- * cannot be removed, and will cause an IllegalArgumentException
+ * cannot be removed, and will cause an IllegalFinPointException
* if attempted.
*
* @param index the fin point index to remove
@@ -173,8 +173,8 @@ public class FreeformFinSet extends FinSet {
*
* Note that this method enforces basic fin shape restrictions (non-negative y,
* first and last point locations) silently, but throws an
- * IllegalArgumentException
if the point causes fin segments to
- * intersect. The calling method should always catch this exception.
+ * IllegalFinPointException
if the point causes fin segments to
+ * intersect.
*
* Moving of the first point in the X-axis is allowed, but this actually moves
* all of the other points the corresponding distance back.
diff --git a/src/net/sf/openrocket/rocketcomponent/InnerTube.java b/src/net/sf/openrocket/rocketcomponent/InnerTube.java
index 901b5cb60..0c30042b0 100644
--- a/src/net/sf/openrocket/rocketcomponent/InnerTube.java
+++ b/src/net/sf/openrocket/rocketcomponent/InnerTube.java
@@ -201,11 +201,26 @@ implements Clusterable, RadialParent, MotorMount {
@Override
public Motor getMotor(String id) {
+ if (id == null)
+ return null;
+
+ // Check whether the id is valid for the current rocket
+ RocketComponent root = this.getRoot();
+ if (!(root instanceof Rocket))
+ return null;
+ if (!((Rocket) root).isMotorConfigurationID(id))
+ return null;
+
return motors.get(id);
}
@Override
public void setMotor(String id, Motor motor) {
+ if (id == null) {
+ if (motor != null) {
+ throw new IllegalArgumentException("Cannot set non-null motor for id null");
+ }
+ }
Motor current = motors.get(id);
if ((motor == null && current == null) ||
(motor != null && motor.equals(current)))
@@ -228,6 +243,7 @@ implements Clusterable, RadialParent, MotorMount {
fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
}
+ @Deprecated
@Override
public int getMotorCount() {
return getClusterCount();
diff --git a/src/net/sf/openrocket/rocketcomponent/MotorMount.java b/src/net/sf/openrocket/rocketcomponent/MotorMount.java
index 20f3c82e9..6735ee742 100644
--- a/src/net/sf/openrocket/rocketcomponent/MotorMount.java
+++ b/src/net/sf/openrocket/rocketcomponent/MotorMount.java
@@ -88,7 +88,8 @@ public interface MotorMount extends ChangeSource {
/**
* Return the motor for the motor configuration. May return null
* if no motor has been set. This method must return null
if ID
- * is null
.
+ * is null
or if the ID is not valid for the current rocket
+ * (or if the component is not part of any rocket).
*
* @param id the motor configuration ID
* @return the motor, or null
if not set.
@@ -107,8 +108,11 @@ public interface MotorMount extends ChangeSource {
/**
* Get the number of similar motors clustered.
*
+ * TODO: HIGH: This should not be used, since the components themselves can be clustered
+ *
* @return the number of motors.
*/
+ @Deprecated
public int getMotorCount();
diff --git a/src/net/sf/openrocket/rocketcomponent/Rocket.java b/src/net/sf/openrocket/rocketcomponent/Rocket.java
index 145e4cf04..9ab110b49 100644
--- a/src/net/sf/openrocket/rocketcomponent/Rocket.java
+++ b/src/net/sf/openrocket/rocketcomponent/Rocket.java
@@ -563,6 +563,9 @@ public class Rocket extends RocketComponent {
* @return whether any motors are defined for it.
*/
public boolean hasMotors(String id) {
+ if (id == null)
+ return false;
+
Iterator iterator = this.deepIterator();
while (iterator.hasNext()) {
RocketComponent c = iterator.next();
@@ -588,6 +591,8 @@ public class Rocket extends RocketComponent {
* @return the configuration name
*/
public String getMotorConfigurationName(String id) {
+ if (!isMotorConfigurationID(id))
+ return "";
String s = motorConfigurationNames.get(id);
if (s == null)
return "";
@@ -607,7 +612,7 @@ public class Rocket extends RocketComponent {
fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
}
-
+
/**
* Return either the motor configuration name (if set) or its description.
*
@@ -617,10 +622,10 @@ public class Rocket extends RocketComponent {
public String getMotorConfigurationNameOrDescription(String id) {
String name;
- name = motorConfigurationNames.get(id);
+ name = getMotorConfigurationName(id);
if (name != null && !name.equals(""))
return name;
-
+
return getMotorConfigurationDescription(id);
}
@@ -636,10 +641,6 @@ public class Rocket extends RocketComponent {
String name;
int motorCount = 0;
- if (!motorConfigurationIDs.contains(id)) {
- throw new IllegalArgumentException("Motor configuration ID does not exist: "+id);
- }
-
// Generate the description
// First iterate over each stage and store the designations of each motor
diff --git a/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java b/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java
index ed0bffb6a..e25ba691d 100644
--- a/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java
+++ b/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java
@@ -308,7 +308,7 @@ public abstract class SymmetricComponent extends BodyComponent implements Radial
} else {
// Hollow piece
final double height = thickness*hyp/l;
- dV = pil*height*(r1+r2-height);
+ dV = MathUtil.max(pil*height*(r1+r2-height), 0);
}
// Add to the volume-related components
@@ -334,11 +334,14 @@ public abstract class SymmetricComponent extends BodyComponent implements Radial
if (planArea > 0)
planCenter /= planArea;
- if (volume == 0) {
- cg = Coordinate.NUL;
+ if (volume < 0.0000000001) { // 0.1 mm^3
+ volume = 0;
+ cg = new Coordinate(length/2, 0, 0, 0);
} else {
// getComponentMass is safe now
- cg = new Coordinate(cgx/volume,0,0,getComponentMass());
+ // Use super.getComponentMass() to ensure only the transition shape mass
+ // is used, not the shoulders
+ cg = new Coordinate(cgx/volume,0,0,super.getComponentMass());
}
}
diff --git a/src/net/sf/openrocket/rocketcomponent/Transition.java b/src/net/sf/openrocket/rocketcomponent/Transition.java
index 5478545fe..f598ad1ee 100644
--- a/src/net/sf/openrocket/rocketcomponent/Transition.java
+++ b/src/net/sf/openrocket/rocketcomponent/Transition.java
@@ -171,6 +171,9 @@ public class Transition extends SymmetricComponent {
}
public void setType(Shape type) {
+ if (type == null) {
+ throw new IllegalArgumentException("BUG: setType called with null argument");
+ }
if (this.type == type)
return;
this.type = type;
diff --git a/src/net/sf/openrocket/unit/FrequencyUnit.java b/src/net/sf/openrocket/unit/FrequencyUnit.java
new file mode 100644
index 000000000..7b5d15cfb
--- /dev/null
+++ b/src/net/sf/openrocket/unit/FrequencyUnit.java
@@ -0,0 +1,25 @@
+package net.sf.openrocket.unit;
+
+public class FrequencyUnit extends GeneralUnit {
+
+
+ public FrequencyUnit(double multiplier, String unit) {
+ super(multiplier, unit);
+ }
+
+
+
+ @Override
+ public double toUnit(double value) {
+ double hz = 1/value;
+ return hz / multiplier;
+ }
+
+
+ @Override
+ public double fromUnit(double value) {
+ double hz = value * multiplier;
+ return 1/hz;
+ }
+
+}
diff --git a/src/net/sf/openrocket/unit/UnitGroup.java b/src/net/sf/openrocket/unit/UnitGroup.java
index ffe80f226..1d8848b27 100644
--- a/src/net/sf/openrocket/unit/UnitGroup.java
+++ b/src/net/sf/openrocket/unit/UnitGroup.java
@@ -57,6 +57,8 @@ public class UnitGroup {
public static final UnitGroup UNITS_COEFFICIENT;
+// public static final UnitGroup UNITS_FREQUENCY;
+
public static final Map UNITS;
@@ -203,6 +205,16 @@ public class UnitGroup {
UNITS_COEFFICIENT = new UnitGroup();
UNITS_COEFFICIENT.addUnit(new FixedPrecisionUnit(""+ZWSP, 0.01)); // zero-width space
+
+ // This is not used by OpenRocket, and not extensively tested:
+// UNITS_FREQUENCY = new UnitGroup();
+// UNITS_FREQUENCY.addUnit(new GeneralUnit(1, "s"));
+// UNITS_FREQUENCY.addUnit(new GeneralUnit(0.001, "ms"));
+// UNITS_FREQUENCY.addUnit(new GeneralUnit(0.000001, MICRO + "s"));
+// UNITS_FREQUENCY.addUnit(new FrequencyUnit(1, "Hz"));
+// UNITS_FREQUENCY.addUnit(new FrequencyUnit(1000, "kHz"));
+// UNITS_FREQUENCY.setDefaultUnit(3);
+
HashMap map = new HashMap();
map.put("NONE", UNITS_NONE);
diff --git a/src/net/sf/openrocket/unit/Value.java b/src/net/sf/openrocket/unit/Value.java
index 54e192790..5b518673f 100644
--- a/src/net/sf/openrocket/unit/Value.java
+++ b/src/net/sf/openrocket/unit/Value.java
@@ -27,6 +27,18 @@ public class Value implements Comparable {
this.value = value;
this.unit = unit;
}
+
+
+ /**
+ * Creates a new Value object using unit group. Currently it simply uses the default
+ * unit of the group, but may later change.
+ *
+ * @param value the value to set.
+ * @param group the group the value belongs to.
+ */
+ public Value(double value, UnitGroup group) {
+ this(value, group.getDefaultUnit());
+ }
/**
diff --git a/src/net/sf/openrocket/unit/ValueComparator.java b/src/net/sf/openrocket/unit/ValueComparator.java
new file mode 100644
index 000000000..a948e9d2d
--- /dev/null
+++ b/src/net/sf/openrocket/unit/ValueComparator.java
@@ -0,0 +1,14 @@
+package net.sf.openrocket.unit;
+
+import java.util.Comparator;
+
+public class ValueComparator implements Comparator {
+
+ public static final ValueComparator INSTANCE = new ValueComparator();
+
+ @Override
+ public int compare(Value o1, Value o2) {
+ return o1.compareTo(o2);
+ }
+
+}
diff --git a/src/net/sf/openrocket/util/Base64.java b/src/net/sf/openrocket/util/Base64.java
index fb8490a79..042d36715 100644
--- a/src/net/sf/openrocket/util/Base64.java
+++ b/src/net/sf/openrocket/util/Base64.java
@@ -16,6 +16,18 @@ public class Base64 {
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
};
private static final char PAD = '=';
+
+// private static final byte[] REVERSE;
+// static {
+// REVERSE = new byte[128];
+// Arrays.fill(REVERSE, (byte)-1);
+// for (int i=0; i<64; i++) {
+// REVERSE[ALPHABET[i]] = (byte)i;
+// }
+// REVERSE['-'] = 62;
+// REVERSE['_'] = 63;
+// REVERSE[PAD] = 0;
+// }
private static final Map REVERSE = new HashMap();
static {
diff --git a/src/net/sf/openrocket/util/Prefs.java b/src/net/sf/openrocket/util/Prefs.java
index dc3e0db40..70a3e558e 100644
--- a/src/net/sf/openrocket/util/Prefs.java
+++ b/src/net/sf/openrocket/util/Prefs.java
@@ -203,7 +203,7 @@ public class Prefs {
public static String getUniqueID() {
String id = PREFNODE.get("id", null);
if (id == null) {
- id = UniqueID.generateHashedID();
+ id = UniqueID.uuid();
PREFNODE.put("id", id);
}
return id;
diff --git a/src/net/sf/openrocket/util/TextUtil.java b/src/net/sf/openrocket/util/TextUtil.java
index f554dc73b..c6a1bb835 100644
--- a/src/net/sf/openrocket/util/TextUtil.java
+++ b/src/net/sf/openrocket/util/TextUtil.java
@@ -2,6 +2,28 @@ package net.sf.openrocket.util;
public class TextUtil {
+ private static final char[] HEX = {
+ '0','1','2','3','4','5','6','7',
+ '8','9','a','b','c','d','e','f'
+ };
+
+
+ /**
+ * Return the bytes formatted as a hexadecimal string. The length of the
+ * string will be twice the number of bytes, with no spacing between the bytes
+ * and lowercase letters utilized.
+ *
+ * @param bytes the bytes to convert.
+ * @return the bytes in hexadecimal notation.
+ */
+ public static final String hexString(byte[] bytes) {
+ StringBuilder sb = new StringBuilder(bytes.length * 2);
+ for (byte b: bytes) {
+ sb.append(HEX[(b >>> 4) & 0xF]);
+ sb.append(HEX[b & 0xF]);
+ }
+ return sb.toString();
+ }
/**
* Return a string of the double value with suitable precision (5 digits).
diff --git a/src/net/sf/openrocket/util/UniqueID.java b/src/net/sf/openrocket/util/UniqueID.java
index e7b56d4ac..1cc082e81 100644
--- a/src/net/sf/openrocket/util/UniqueID.java
+++ b/src/net/sf/openrocket/util/UniqueID.java
@@ -1,12 +1,8 @@
package net.sf.openrocket.util;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
-import net.sf.openrocket.gui.main.ExceptionHandler;
-
public class UniqueID {
private static AtomicInteger nextId = new AtomicInteger(1);
@@ -34,34 +30,4 @@ public class UniqueID {
return UUID.randomUUID().toString();
}
-
- /**
- * Return a hashed unique ID that contains no information whatsoever of the
- * originating computer.
- *
- * @return a unique identifier string that contains no information about the computer.
- */
- public static String generateHashedID() {
- String id = UUID.randomUUID().toString();
-
- try {
- MessageDigest algorithm = MessageDigest.getInstance("MD5");
- algorithm.reset();
- algorithm.update(id.getBytes());
- byte[] digest = algorithm.digest();
-
- StringBuilder sb = new StringBuilder();
- for (byte b: digest) {
- sb.append(String.format("%02X", 0xFF & b));
- }
- id = sb.toString();
-
- } catch (NoSuchAlgorithmException e) {
- ExceptionHandler.handleErrorCondition(e);
- id = "" + id.hashCode();
- }
-
- return id;
- }
-
}
diff --git a/src/net/sf/openrocket/utils/MotorPrinter.java b/src/net/sf/openrocket/utils/MotorPrinter.java
index 194e15aff..ed6bdb7e2 100644
--- a/src/net/sf/openrocket/utils/MotorPrinter.java
+++ b/src/net/sf/openrocket/utils/MotorPrinter.java
@@ -37,6 +37,7 @@ public class MotorPrinter {
System.out.printf(" Total impulse: %.2f Ns\n", m.getTotalImpulse());
System.out.println(" Diameter: " + m.getDiameter()*1000 + " mm");
System.out.println(" Length: " + m.getLength()*1000 + " mm");
+ System.out.println(" Digest: " + m.getDigestString());
if (m instanceof ThrustCurveMotor) {
ThrustCurveMotor tc = (ThrustCurveMotor)m;
diff --git a/test/net/sf/openrocket/motor/MotorDigestTest.java b/test/net/sf/openrocket/motor/MotorDigestTest.java
new file mode 100644
index 000000000..916ab12c4
--- /dev/null
+++ b/test/net/sf/openrocket/motor/MotorDigestTest.java
@@ -0,0 +1,76 @@
+package net.sf.openrocket.motor;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import net.sf.openrocket.motor.MotorDigest.DataType;
+import net.sf.openrocket.util.TextUtil;
+
+import org.junit.Test;
+
+
+public class MotorDigestTest {
+
+ private static final double[] timeArray = {
+ 0.0, 0.123456789, 0.4115, Math.nextAfter(Math.nextAfter(1.4445, 0), 0)
+ };
+
+ private static final double[] massArray = {
+ 0.54321, 0.43211
+ };
+
+ private static final double[] thrustArray = {
+ 0.0, 0.2345678, 9999.3335, 0.0
+ };
+
+ private static final int[] intData = {
+ // Time (ms)
+ 0, 4, 0, 123, 412, 1445,
+ // Mass specific (0.1g)
+ 1, 2, 5432, 4321,
+ // Thrust (mN)
+ 5, 4, 0, 235, 9999334, 0
+ };
+
+
+ @Test
+ public void testMotorDigest() throws NoSuchAlgorithmException {
+
+ MessageDigest correct = MessageDigest.getInstance("MD5");
+ for (int value: intData) {
+ correct.update((byte) ((value >>> 24) & 0xFF));
+ correct.update((byte) ((value >>> 16) & 0xFF));
+ correct.update((byte) ((value >>> 8) & 0xFF));
+ correct.update((byte) (value & 0xFF));
+ }
+
+ MotorDigest motor = new MotorDigest();
+ motor.update(DataType.TIME_ARRAY, timeArray);
+ motor.update(DataType.MASS_SPECIFIC, massArray);
+ motor.update(DataType.FORCE_PER_TIME, thrustArray);
+
+
+ assertEquals(TextUtil.hexString(correct.digest()), motor.getDigest());
+ }
+
+
+ @Test
+ public void testCommentDigest() throws NoSuchAlgorithmException, UnsupportedEncodingException {
+
+ assertEquals(md5("Hello world!"), MotorDigest.digestComment("Hello world! "));
+ assertEquals(md5("Hello world!"), MotorDigest.digestComment("\nHello\tworld!\n\r"));
+ assertEquals(md5("Hello world!"), MotorDigest.digestComment("Hello\r\r\r\nworld!"));
+ assertEquals(md5("Hello\u00e4 world!"), MotorDigest.digestComment("Hello\u00e4\r\r\nworld!"));
+
+ }
+
+
+ private static String md5(String source)
+ throws NoSuchAlgorithmException, UnsupportedEncodingException {
+ MessageDigest digest = MessageDigest.getInstance("MD5");
+ return TextUtil.hexString(digest.digest(source.getBytes("UTF-8")));
+ }
+}
diff --git a/test/net/sf/openrocket/util/TextUtilTest.java b/test/net/sf/openrocket/util/TextUtilTest.java
index 73930cf87..3b7385f00 100644
--- a/test/net/sf/openrocket/util/TextUtilTest.java
+++ b/test/net/sf/openrocket/util/TextUtilTest.java
@@ -3,9 +3,38 @@ package net.sf.openrocket.util;
import static java.lang.Math.PI;
import static org.junit.Assert.assertEquals;
+import java.util.Random;
+
import org.junit.Test;
public class TextUtilTest {
+
+ @Test
+ public void textHexString() {
+ assertEquals("", TextUtil.hexString(new byte[0]));
+ assertEquals("00", TextUtil.hexString(new byte[] { 0x00 }));
+ assertEquals("ff", TextUtil.hexString(new byte[] { (byte) 0xff }));
+
+ for (int i=0; i <= 0xff; i++) {
+ assertEquals(String.format("%02x", i), TextUtil.hexString(new byte[] { (byte) i }));
+ }
+
+ assertEquals("0f1e2d3c4b5a6978", TextUtil.hexString(new byte[] {
+ 0x0f, 0x1e, 0x2d, 0x3c, 0x4b, 0x5a, 0x69, 0x78
+ }));
+
+ Random rnd = new Random();
+ for (int count=0; count<10; count++) {
+ int n = rnd.nextInt(100);
+ byte[] bytes = new byte[n];
+ rnd.nextBytes(bytes);
+ StringBuilder sb = new StringBuilder();
+ for (byte b: bytes) {
+ sb.append(String.format("%02x", b & 0xFF));
+ }
+ assertEquals(sb.toString(), TextUtil.hexString(bytes));
+ }
+ }
@Test
public void specialCaseTest() {
diff --git a/test/net/sf/openrocket/util/UniqueIDTest.java b/test/net/sf/openrocket/util/UniqueIDTest.java
index eeec61533..c77da324e 100644
--- a/test/net/sf/openrocket/util/UniqueIDTest.java
+++ b/test/net/sf/openrocket/util/UniqueIDTest.java
@@ -26,25 +26,4 @@ public class UniqueIDTest {
assertNotSame(id, UniqueID.uuid());
}
- @Test
- public void hashedTest() {
- String id = UniqueID.generateHashedID();
- assertNotNull(id);
-
- boolean matchhigh = false;
- boolean matchlow = false;
- for (int i=0; i<100; i++) {
- String newid = UniqueID.generateHashedID();
- assertNotNull(newid);
- assertNotSame(id, newid);
- assertTrue(newid.matches("^[0-9a-fA-F]{32}$"));
-
- // Check that both high and low values occur
- matchhigh = matchhigh || newid.matches("^([0-9a-fA-F][0-9a-fA-F])*[A-F].*");
- matchlow = matchlow || newid.matches("^([0-9a-fA-F][0-9a-fA-F])*[0-4].*");
- }
- assertTrue(matchhigh);
- assertTrue(matchlow);
- }
-
}