diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 32bd80d..ac413b3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,5 +21,5 @@ For the best chance of getting your changes incorporated into the code base, ple * Follow the coding style already established - same indentation, line spacing, naming convention etc. * ALWAYS provide JUnit tests for your changes - copy existing ones, modify the current versions but there must be supporting unit tests -* Thorougly comment your code and provide proper JavaDoc headers, even for private methods +* Thoroughly comment your code and provide proper JavaDoc headers, even for private methods diff --git a/src/main/java/com/ghgande/j2mod/modbus/facade/ModbusTCPMaster.java b/src/main/java/com/ghgande/j2mod/modbus/facade/ModbusTCPMaster.java index f48af3a..53559bc 100644 --- a/src/main/java/com/ghgande/j2mod/modbus/facade/ModbusTCPMaster.java +++ b/src/main/java/com/ghgande/j2mod/modbus/facade/ModbusTCPMaster.java @@ -21,6 +21,7 @@ import com.ghgande.j2mod.modbus.net.TCPMasterConnection; import java.net.InetAddress; +import java.net.SocketException; import java.net.UnknownHostException; /** @@ -194,4 +195,32 @@ public AbstractModbusTransport getTransport() { public boolean isConnected() { return connection != null && connection.isConnected(); } + + /** + * A {@link ModbusTCPMaster} is equal if the underlying {@link TCPMasterConnection} is equal. There can't be 2 + * un-identical {@link ModbusTCPMaster}s with the same connection at the same time. One of the two, will raise a + * {@link SocketException} if you start both at the same time, since the socket is only usable by exactly on entity + * + * @param obj Entity to be checked for equality + * @return true if object is equal, false otherwise + */ + @Override + public boolean equals(Object obj) { + if (obj == null || ModbusTCPMaster.class != obj.getClass()) { + return false; + } else { + ModbusTCPMaster other = (ModbusTCPMaster) obj; + return this == obj || this.connection.equals(other.connection); + } + } + + /** + * The unique value of the {@link ModbusTCPMaster} is calculated from the unique value of the {@link TCPMasterConnection} + * + * @return Unique integer hash code + */ + @Override + public int hashCode() { + return connection.hashCode(); + } } \ No newline at end of file diff --git a/src/main/java/com/ghgande/j2mod/modbus/net/TCPMasterConnection.java b/src/main/java/com/ghgande/j2mod/modbus/net/TCPMasterConnection.java index 14e2977..1352964 100644 --- a/src/main/java/com/ghgande/j2mod/modbus/net/TCPMasterConnection.java +++ b/src/main/java/com/ghgande/j2mod/modbus/net/TCPMasterConnection.java @@ -27,6 +27,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; +import java.util.Objects; /** * Class that implements a TCPMasterConnection. @@ -335,4 +336,33 @@ public void setUseRtuOverTcp(boolean useRtuOverTcp) throws Exception { prepareTransport(useRtuOverTcp); } } + + /** + * A {@link TCPMasterConnection} is equal if {@link InetAddress} & port are equal. There is no way how two + * different {@link TCPMasterConnection}s can use the same {@link Socket} at the same time, therefore this is good + * enough for an equality test + * + * @param obj Entity to be checked for equality + * @return true if object is equal, false otherwise + */ + @Override + public boolean equals(Object obj) { + if (obj == null || TCPMasterConnection.class != obj.getClass()) { + return false; + } else { + TCPMasterConnection other = (TCPMasterConnection) obj; + return this == obj || this.address.equals(other.address) && this.port == other.port; + } + } + + /** + * The unique value of the {@link TCPMasterConnection} is calculated from the unique values of the + * {@link InetAddress} & port + * + * @return Unique integer hash code + */ + @Override + public int hashCode() { + return Objects.hash(address, port); + } } diff --git a/src/test/java/com/ghgande/j2mod/modbus/TestModbusTCPMasterEquality.java b/src/test/java/com/ghgande/j2mod/modbus/TestModbusTCPMasterEquality.java new file mode 100644 index 0000000..424e283 --- /dev/null +++ b/src/test/java/com/ghgande/j2mod/modbus/TestModbusTCPMasterEquality.java @@ -0,0 +1,51 @@ +package com.ghgande.j2mod.modbus; + +import com.ghgande.j2mod.modbus.facade.ModbusTCPMaster; +import com.ghgande.j2mod.modbus.utils.AbstractTestModbusTCPMaster; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.*; + +/** + * Testing class for {@link ModbusTCPMaster#equals} & {@link ModbusTCPMaster#hashCode} methods + */ +public class TestModbusTCPMasterEquality extends AbstractTestModbusTCPMaster { + + private static final Logger logger = LoggerFactory.getLogger(AbstractTestModbusTCPMaster.class); + public static final String LOCALHOST_2 = "127.0.0.2"; + public static final int PORT_2 = 2503; + protected static ModbusTCPMaster master_2, master_3; + + @Test + public void testEquality() { + master_2 = new ModbusTCPMaster(LOCALHOST_2, PORT); + master_3 = new ModbusTCPMaster(LOCALHOST, PORT_2); + + assertThat("2 unequal Modbus TCP masters identified as equal", master, not(equalTo(master_2))); + assertThat("2 unequal Modbus TCP masters identified as equal", master, not(equalTo(master_3))); + assertThat("2 unequal Modbus TCP masters identified as equal", master_2, not(equalTo(master_3))); + + assertThat("2 equal Modbus TCP masters identified as unequal", master, equalTo(master)); + assertThat("2 equal Modbus TCP masters identified as unequal", master_2, equalTo(master_2)); + assertThat("2 equal Modbus TCP masters identified as unequal", master_3, equalTo(master_3)); + } + + @Test + public void testHashCode() { + master_2 = new ModbusTCPMaster(LOCALHOST_2, PORT); + master_3 = new ModbusTCPMaster(LOCALHOST, PORT_2); + + assertThat("2 unequal Modbus TCP masters had same hash code", master.hashCode(), not(equalTo(master_2.hashCode()))); + assertThat("2 unequal Modbus TCP masters had same hash code", master.hashCode(), not(equalTo(master_3.hashCode()))); + assertThat("2 unequal Modbus TCP masters had same hash code", master_2.hashCode(), not(equalTo(master_3.hashCode()))); + + assertThat("2 equal Modbus TCP masters had NOT same hash code", master.hashCode(), equalTo(master.hashCode())); + assertThat("2 equal Modbus TCP masters had NOT same hash code", master_2.hashCode(), equalTo(master_2.hashCode())); + assertThat("2 equal Modbus TCP masters had NOT same hash code", master_3.hashCode(), equalTo(master_3.hashCode())); + } +}