Commit ffb4f11a by Tuukka Kivilahti

Merge branch 'PlaceComparator' into 'master'

Numeric comparator

Numeric comparator to compare strings numerically.
modify Place numeric comparison so that it handles cases where name or id is null correctly.

- Tää on kauniimpi kuin vanha, Kattoi @tkfftk
2 parents a1745bab cf7427d7
...@@ -54,37 +54,32 @@ public class GenericEntity extends EntityEquals implements ModelInterface, Entit ...@@ -54,37 +54,32 @@ public class GenericEntity extends EntityEquals implements ModelInterface, Entit
return ""; return "";
} }
} }
@Transient @Transient
public void setMetaStringValue(String key, String value) { public void setMetaStringValue(String key, String value) {
JsonObjectBuilder metaBuilder = Json.createObjectBuilder(); JsonObjectBuilder metaBuilder = Json.createObjectBuilder();
if(getMeta() != null) { if (getMeta() != null) {
for(String valKey : getMeta().keySet()) { for (String valKey : getMeta().keySet()) {
if(!valKey.equals(key)) { if (!valKey.equals(key)) {
metaBuilder.add(valKey, getMeta().get(valKey)); metaBuilder.add(valKey, getMeta().get(valKey));
} }
} }
} }
metaBuilder.add(key, value); metaBuilder.add(key, value);
setMeta(metaBuilder.build()); setMeta(metaBuilder.build());
} }
} @Transient
protected Integer getNonNullId()
{
if (getId() != null) {
return getId();
}
return getRndid();
}
}
...@@ -14,6 +14,8 @@ import javax.persistence.Temporal; ...@@ -14,6 +14,8 @@ import javax.persistence.Temporal;
import javax.persistence.TemporalType; import javax.persistence.TemporalType;
import javax.persistence.Transient; import javax.persistence.Transient;
import fi.codecrew.moya.utilities.NumericStringComparator;
/** /**
* *
*/ */
...@@ -276,71 +278,35 @@ public class Place extends GenericEntity implements Comparable<Place> { ...@@ -276,71 +278,35 @@ public class Place extends GenericEntity implements Comparable<Place> {
} }
/** /**
* Note: this class has a natural ordering that is inconsistent with equals. * Note: this class has a natural ordering that is inconsistent with equals.
*/ */
@Override @Override
@Transient @Transient
public int compareTo(Place o) { public int compareTo(Place o) {
if (this.equals(o)) {
if(this.getName().equals(o.getName())) {
return 0;
}
// check empty string etc.
if(o.getName().length() == 0) {
if(this.getName().length() > 0)
return 1;
return 0; return 0;
} }
if(this.getName().length() == 0 && o.getName().length() > 0) { // Check null values;
return -1; // Null is smaller than non null value.
} if (this.getName() == null || o.getName() == null) {
if (this.getName() == null) {
// check prefixes
String splitted[] = o.getName().split("[0-9]");
if(splitted.length > 0 && splitted[0].length() > 0) {
if(splitted[0].length() > this.getName().length()) {
return this.getName().compareTo(splitted[0]);
}
} else {
return 1;
}
String myPrefix = this.getName().substring(0, splitted[0].length());
if(myPrefix.compareTo(splitted[0]) != 0)
return myPrefix.compareTo(splitted[0]);
// prefixes are same, find numbers and check them
String otherNumber = o.getName().substring(splitted[0].length());
String myNumber = this.getName().substring(splitted[0].length());
try {
int other = Integer.parseInt(otherNumber);
int me = Integer.parseInt(myNumber);
if(other < me) {
return 1; return 1;
} }
if (o.getName() == null) {
if(other > me)
return -1; return -1;
}
// both names are null. Compare IDs
return this.getNonNullId().compareTo(o.getNonNullId());
}
if (this.getName().equals(o.getName())) {
return 0; return 0;
} catch(NumberFormatException x) {
return myNumber.compareTo(otherNumber);
} }
return NumericStringComparator.numericCompare(this.getName(), o.getName());
} }
} }
package fi.codecrew.moya.utilities;
import java.math.BigInteger;
import java.util.Comparator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NumericStringComparator implements Comparator<String> {
// \D non numeric
// \d numeric
private static final Pattern NUMERIC_COMPARE_PATTERN = Pattern.compile("(\\D*)(\\d*)");
public static int numericCompare(String o1, String o2) {
Matcher m1 = NUMERIC_COMPARE_PATTERN.matcher(o1);
Matcher m2 = NUMERIC_COMPARE_PATTERN.matcher(o2);
Integer.compare(1, 2);
// Match fails only in end of either string
while (m1.find() && m2.find()) {
// Compare non digit part of the match (can be empty, but not null)
int nonDigitCompare = m1.group(1).compareTo(m2.group(1));
if (0 != nonDigitCompare) {
return nonDigitCompare;
}
// compare digit part of the match.
if (m1.group(2).isEmpty()) {
return m2.group(2).isEmpty() ? 0 : -1;
} else if (m2.group(2).isEmpty()) {
return +1;
}
// Integer might overflow.. Unlikely, but possible. Use bigint
BigInteger n1 = new BigInteger(m1.group(2));
BigInteger n2 = new BigInteger(m2.group(2));
int numberCompare = n1.compareTo(n2);
if (0 != numberCompare) {
return numberCompare;
}
}
// when we hit the end of either string
return m1.hitEnd() && m2.hitEnd() ? 0 : m1.hitEnd() ? -1 : +1;
}
@Override
public int compare(String o1, String o2) {
return numericCompare(o1, o2);
}
}
...@@ -35,10 +35,17 @@ public abstract class EntityEquals { ...@@ -35,10 +35,17 @@ public abstract class EntityEquals {
return ret; return ret;
} }
private Integer getRndid() { @Transient
// Always negative!
protected Integer getRndid() {
if (rndid == null) { if (rndid == null) {
Random rng = new Random(new Date().getTime()); Random rng = new Random(new Date().getTime());
rndid = Integer.valueOf(rng.nextInt()); int i = 0;
while (i == 0)
i = rng.nextInt();
if (i > 0)
i *= -1;
rndid = Integer.valueOf(i);
} }
return rndid; return rndid;
} }
......
package fi.codecrew.moya.utilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.Test;
public class StringUtilsTest {
private static final Logger logger = LoggerFactory.getLogger(StringUtilsTest.class);
@Test
public void numericTest1()
{
Assert.assertTrue(NumericStringComparator.numericCompare("ABC123", "ABC123") == 0);
Assert.assertTrue(NumericStringComparator.numericCompare("123ABC", "123ABC") == 0);
Assert.assertTrue(NumericStringComparator.numericCompare("A10", "A9") > 0);
Assert.assertTrue(NumericStringComparator.numericCompare("10A", "9A") > 0);
Assert.assertTrue(NumericStringComparator.numericCompare("10A11", "10A9") > 0);
Assert.assertTrue(NumericStringComparator.numericCompare("10A11", "9A9") > 0);
Assert.assertTrue(NumericStringComparator.numericCompare("10A", "10A") == 0);
Assert.assertTrue(NumericStringComparator.numericCompare("10A1234", "10A1234") == 0);
// Lexicographically... ie 'a'< 'b'
Assert.assertTrue(NumericStringComparator.numericCompare("A", "a") < 0);
Assert.assertTrue(NumericStringComparator.numericCompare("A", "B") < 0);
Assert.assertTrue(NumericStringComparator.numericCompare("a", "9") > 0);
Assert.assertTrue(NumericStringComparator.numericCompare("A", "9") > 0);
Assert.assertTrue(NumericStringComparator.numericCompare("10", "9") > 0);
}
@Test(expectedExceptions = NullPointerException.class)
public void nulltest1() {
Assert.assertTrue(NumericStringComparator.numericCompare(null, "asd") == 0);
}
@Test(expectedExceptions = NullPointerException.class)
public void nulltest2() {
Assert.assertTrue(NumericStringComparator.numericCompare("asd", null) == 0);
}
@Test(expectedExceptions = NullPointerException.class)
public void nulltest3() {
Assert.assertTrue(NumericStringComparator.numericCompare(null, null) == 0);
}
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!