Skip to content

Commit

Permalink
fix: Add numeric comparator that preserves leading zeros
Browse files Browse the repository at this point in the history
  • Loading branch information
rhwood committed Nov 27, 2019
1 parent 4c77ce1 commit e2540b0
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.ResourceBundle;
import jmri.InstanceManager;
import jmri.NamedBean;
import jmri.util.NamedBeanPreferNumericComparator;

/**
* Lightweight class to denote that a system is active, and provide general
Expand Down Expand Up @@ -270,7 +271,7 @@ protected ResourceBundle getActionModelResourceBundle() {

@Override
public <B extends NamedBean> Comparator<B> getNamedBeanComparator(Class<B> type) {
return (o1, o2) -> o1.getSystemName().compareTo(o2.getSystemName());
return new NamedBeanPreferNumericComparator<>();
}

@Override
Expand All @@ -286,6 +287,6 @@ public void dispose() {
super.dispose();
}

private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(InternalSystemConnectionMemo.class);
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(InternalSystemConnectionMemo.class);

}
7 changes: 3 additions & 4 deletions java/src/jmri/util/NamedBeanComparator.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package jmri.util;

import java.util.Comparator;

import jmri.NamedBean;

/**
Expand All @@ -21,10 +23,7 @@
*
* @param <B> supported type of NamedBean
*/
public class NamedBeanComparator <B extends NamedBean> implements java.util.Comparator<B> {

public NamedBeanComparator() {
}
public class NamedBeanComparator <B extends NamedBean> implements Comparator<B> {

@Override
public int compare(B n1, B n2) {
Expand Down
31 changes: 31 additions & 0 deletions java/src/jmri/util/NamedBeanPreferNumericComparator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package jmri.util;

import java.util.Comparator;

import jmri.NamedBean;

/**
* Compare two NamedBeans using the {@link PreferNumericComparator} against
* {@link NamedBean#getSystemName()} for each NamedBean.
* <p>
* If the requirement is that {@link Comparator#compare(Object, Object)} return
* 0 for two numerically identical NamedBean System Names (i.e.
* {@code IT42 == IT0042}), use {@link NamedBeanComparator}, but if the
* requirement is that System Names should be numerically ordered, but that
* non-identical representations of numbers should be different, (i.e.
* {@code IT42 != IT0042}, but order should be
* {@code IT3, IT4, IT5, IT42, IT0042, IT50}), use this Comparator.
*
* @author Randall Wood Copyright 2019
* @param <B> the type of NamedBean to compare
*/
public class NamedBeanPreferNumericComparator<B extends NamedBean> extends NamedBeanComparator<B> {

private final PreferNumericComparator comparator = new PreferNumericComparator();

@Override
public int compare(B n1, B n2) {
return comparator.compare(n1.getSystemName(), n2.getSystemName());
}

}
30 changes: 24 additions & 6 deletions java/src/jmri/util/PreferNumericComparator.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
package jmri.util;

import java.util.Comparator;

/**
* Originally, this attempted to compare as Integer, and then alphanumeric.
* It's now deprecated in favor of the more general AlphanumComparator
* @deprecated 4.11.1 use AlphanumComparator
*
* @author Bob Jacobsen Copyright (C) 2013, 2017
* Perform an comparison using {@link AlphanumComparator}, followed up with a
* standard String comparison if
* {@link AlphanumComparator#compare(String, String)} returns 0.
* <p>
* If the requirement is that {@link Comparator#compare(Object, Object)} return
* 0 for two numerically identical Strings (i.e. {@code 42 == 0042}), use
* {@link AlphanumComparator}, but if the requirement is that Strings should be
* numerically ordered, but that non-identical representations should be
* different, (i.e. {@code 42 != 0042}, but order should be
* {@code 3, 4, 5, 42, 0042, 50}), use this Comparator, since the standard
* String comparator will not order numbers correctly.
*
* @author Randall Wood Copyright 2019
*/
@Deprecated // in 4.11.1 use AlphanumComparator
public class PreferNumericComparator extends AlphanumComparator {

@Override
public int compare(String s1, String s2) {
int comparison = super.compare(s1, s2);
if (comparison == 0) {
return s1.compareTo(s2);
}
return comparison;
}
}
119 changes: 119 additions & 0 deletions java/test/jmri/util/NamedBeanPreferNumericComparatorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package jmri.util;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import jmri.*;

/**
* @author Paul Bender Copyright (C) 2017
*/
public class NamedBeanPreferNumericComparatorTest {

@Test
public void testOneLetterCases() {
NamedBeanPreferNumericComparator<Turnout> t = new NamedBeanPreferNumericComparator<>();

Turnout it1 = InstanceManager.getDefault(TurnoutManager.class).provideTurnout("IT1");
Turnout it10 = InstanceManager.getDefault(TurnoutManager.class).provideTurnout("IT10");
Turnout it2 = InstanceManager.getDefault(TurnoutManager.class).provideTurnout("IT2");
Turnout it01 = InstanceManager.getDefault(TurnoutManager.class).provideTurnout("IT01");

Assert.assertEquals("IT1 == IT1", 0, t.compare(it1, it1));

Assert.assertEquals("IT1 < IT2", -1, t.compare(it1, it2));
Assert.assertEquals("IT2 > IT1", +1, t.compare(it2, it1));

Assert.assertEquals("IT10 > IT2", +1, t.compare(it10, it2));
Assert.assertEquals("IT2 < IT10", -1, t.compare(it2, it10));

Assert.assertNotEquals("IT1 != IT01", 0, t.compare(it1, it01));
Assert.assertEquals("IT01 < IT1", -1, t.compare(it01, it1));
Assert.assertEquals("IT1 > IT01", +1, t.compare(it1, it01));
}

@Test
public void testTwoLetterCases() {
NamedBeanPreferNumericComparator<Turnout> t = new NamedBeanPreferNumericComparator<>();

Turnout i2t1 = InstanceManager.getDefault(TurnoutManager.class).provideTurnout("I2T1");
Turnout i2t10 = InstanceManager.getDefault(TurnoutManager.class).provideTurnout("I2T10");
Turnout i2t2 = InstanceManager.getDefault(TurnoutManager.class).provideTurnout("I2T2");

Assert.assertEquals("I2T1 == I2T1", 0, t.compare(i2t1, i2t1));

Assert.assertEquals("I2T1 < I2T2", -1, t.compare(i2t1, i2t2));
Assert.assertEquals("I2T2 > I2T1", +1, t.compare(i2t2, i2t1));

Assert.assertEquals("I2T10 > I2T2", +1, t.compare(i2t10, i2t2));
Assert.assertEquals("I2T2 < I2T10", -1, t.compare(i2t2, i2t10));
}

@Test
public void testThreeLetterCases() {
NamedBeanPreferNumericComparator<Turnout> t = new NamedBeanPreferNumericComparator<>();

Turnout i23t1 = InstanceManager.getDefault(TurnoutManager.class).provideTurnout("I23T1");
Turnout i23t10 = InstanceManager.getDefault(TurnoutManager.class).provideTurnout("I23T10");
Turnout i23t2 = InstanceManager.getDefault(TurnoutManager.class).provideTurnout("I23T2");

Assert.assertEquals("I23T1 == I23T1", 0, t.compare(i23t1, i23t1));

Assert.assertEquals("I23T1 < I23T2", -1, t.compare(i23t1, i23t2));
Assert.assertEquals("I23T2 > I23T1", +1, t.compare(i23t2, i23t1));

Assert.assertEquals("I23T10 > I23T2", +1, t.compare(i23t10, i23t2));
Assert.assertEquals("I23T2 < I23T10", -1, t.compare(i23t2, i23t10));
}

boolean hit = false;

@Test
public void testSystemSpecificCase() {
NamedBeanComparator<Turnout> t = new NamedBeanComparator<>();

// this just checks that the local sort is called
Turnout it1 = InstanceManager.getDefault(TurnoutManager.class).provideTurnout("IT1");
Turnout it2 = new jmri.implementation.AbstractTurnout("IT2") {

@Override
protected void forwardCommandChangeToLayout(int s) {
}

@Override
protected void turnoutPushbuttonLockout(boolean b) {
}

@Override
public int compareSystemNameSuffix(String suffix1, String suffix2, jmri.NamedBean n) {
hit = true;
return super.compareSystemNameSuffix(suffix1, suffix2, n);
}
};

hit = false;
Assert.assertEquals("IT1 < IT2", -1, t.compare(it1, it2));
Assert.assertFalse(hit);

hit = false;
Assert.assertEquals("IT2 < IT1", +1, t.compare(it2, it1));
Assert.assertTrue(hit);
}

// The minimal setup for log4J
@Before
public void setUp() {
JUnitUtil.setUp();
}

@After
public void tearDown() {
JUnitUtil.tearDown();
}

// private final static Logger log =
// LoggerFactory.getLogger(NamedBeanComparatorTest.class);

}
1 change: 1 addition & 0 deletions java/test/jmri/util/PackageTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
LocoAddressComparatorTest.class,
MouseInputAdapterInstallerTest.class,
NamedBeanComparatorTest.class,
NamedBeanPreferNumericComparatorTest.class,
NamedBeanUserNameComparatorTest.class,
NonNullArrayListTest.class,
NoArchiveFileFilterTest.class,
Expand Down

0 comments on commit e2540b0

Please sign in to comment.