deleted Contacts object, added contacts as part of Contact class

additionally:
* updated serialization method to not recurse endlessly
* refactored type of contacts from List to Set
This commit is contained in:
delvh 2020-04-01 18:09:07 +02:00
parent c41089e005
commit 0d3316be4a
10 changed files with 229 additions and 129 deletions

View File

@ -1,28 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="org.eclipse.jst.component.nondependency" value=""/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="org.eclipse.jst.component.nondependency" value=""/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@ -23,6 +23,7 @@
<directory>src/main/resources</directory>
</resource>
</resources>
<testSourceDirectory>src/test/java</testSourceDirectory>
<pluginManagement>
<plugins>
<plugin>

View File

@ -2,6 +2,7 @@ package envoy.data;
import java.io.Serializable;
import java.util.Objects;
import java.util.Set;
/**
* This class is the superclass for both {@link User} and {@link Group}.<br>
@ -16,21 +17,24 @@ import java.util.Objects;
*/
public abstract class Contact implements Serializable {
private final long id;
private String name;
private final long id;
private final transient Set<? extends Contact> contacts;
private String name;
private static final long serialVersionUID = 0L;
/**
* Creates a new instance of a {@link Contact}.
*
* @param id the ID of this contact
* @param name the name of this contact
* @param id the ID of this contact
* @param name the name of this contact
* @param contacts the {@link Contacts} of this {@link Contact}
* @since Envoy Common v0.1-beta
*/
public Contact(long id, String name) {
this.id = id;
this.name = name;
public Contact(long id, String name, Set<? extends Contact> contacts) {
this.id = id;
this.name = name;
this.contacts = contacts;
}
/**
@ -55,7 +59,7 @@ public abstract class Contact implements Serializable {
* {@inheritDoc}
*/
@Override
public String toString() { return String.format("Contact[id=%d,name=%s]", id, name); }
public String toString() { return String.format("Contact[id=%d,name=%s, contacts=%s]", id, name, contacts); }
/**
* {@inheritDoc}
@ -72,4 +76,10 @@ public abstract class Contact implements Serializable {
if (!(obj instanceof Contact)) return false;
return id == ((Contact) obj).id;
}
/**
* @return the contacts of this {@link Contact}
* @since Envoy Common v0.1-beta
*/
public Set<? extends Contact> getContacts() { return contacts; }
}

View File

@ -1,36 +0,0 @@
package envoy.data;
import java.io.Serializable;
import java.util.List;
/**
* Project: <strong>envoy-common</strong><br>
* File: <strong>Contacts.java</strong><br>
* Created: <strong>02.01.2020</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy Common v0.2-alpha
*/
public class Contacts implements Serializable {
private final List<Contact> contacts;
private static final long serialVersionUID = 0L;
/**
* Creates an instance of {@link Contacts}.
*
* @param contacts the contact list
* @since Envoy Common v0.2-alpha
*/
public Contacts(List<Contact> contacts) { this.contacts = contacts; }
@Override
public String toString() { return String.format("Contacts[%s]", contacts); }
/**
* @return a list of users messages can be sent to
* @since Envoy Common v0.2-alpha
*/
public List<Contact> getContacts() { return contacts; }
}

View File

@ -1,8 +1,9 @@
package envoy.data;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashSet;
import java.util.Set;
/**
* Project: <strong>envoy-common</strong><br>
@ -12,50 +13,44 @@ import java.util.StringJoiner;
* @author Leon Hofmeister
* @since Envoy Common v0.1-beta
*/
public class Group extends Contact {
// TODO add admins
private final List<Long> memberIDs;
public final class Group extends Contact {
private static final long serialVersionUID = 0L;
/**
* Creates a new instance of a {@link Group}.
* Creates a new instance of a {@link Group} without any members.
*
* @param id the ID of this group
* @param name the name of this group
* @since Envoy Common v0.1-beta
*/
public Group(long id, String name) { this(id, name, new ArrayList<>()); }
public Group(long id, String name) { this(id, name, new HashSet<User>()); }
/**
* Creates a new instance of a {@link Group}.
* Creates an instance of a {@link Group}.
*
* @param id the ID of this group
* @param name the name of this group
* @param memberIDs the IDs of all members that should be preinitialized
* @param id the ID of this group
* @param name the name of this group
* @param members all members that should be preinitialized
* @since Envoy Common v0.1-beta
*/
public Group(long id, String name, List<Long> memberIDs) {
super(id, name);
this.memberIDs = memberIDs;
public Group(long id, String name, Set<User> members) { super(id, name, members); }
private void readObject(ObjectInputStream inputStream) throws Exception {
inputStream.defaultReadObject();
var contacts = Contact.class.getDeclaredField("contacts");
contacts.setAccessible(true);
contacts.set(this, inputStream.readObject());
}
/**
* @return the IDs of all members of this group
* @since Envoy Common v0.1-beta
*/
public List<Long> getMemberIDs() { return memberIDs; }
private void writeObject(ObjectOutputStream outputStream) throws Exception {
outputStream.defaultWriteObject();
getContacts().forEach(user -> user.serializeContacts(false));
outputStream.writeObject(getContacts());
/**
* {@inheritDoc}
*/
}
@SuppressWarnings("unchecked")
@Override
public String toString() {
var joiner = new StringJoiner(",", "Group[id=", "]");
joiner.add("id=" + getID());
joiner.add("name=" + getName());
joiner.add("members=" + getMemberIDs().size());
return joiner.toString();
}
public Set<User> getContacts() { return (Set<User>) super.getContacts(); }
}

View File

@ -2,6 +2,7 @@ package envoy.data;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import envoy.data.Message.MessageStatus;
@ -102,6 +103,35 @@ public class MessageBuilder {
return new Message(id, senderID, recipientID, creationDate, text, attachment, status, forwarded);
}
/**
* Creates an instance of {@link GroupMessage} with the previously supplied
* values. <br>
* <b> Sets all member statuses to {@link MessageStatus#WAITING}.</b><br>
* If a mandatory value is not set, a default value will be used
* instead:<br>
* <br>
* <table border="1">
* <tr>
* <td>{@code date}</td>
* <td>{@code new Date()}</td>
* <tr>
* <tr>
* <td>{@code text}</td>
* <td>{@code ""}</td>
* <tr>
* </table>
*
* @param group the {@link Group} that is used to fill the map of member
* statuses
* @return a new instance of {@link GroupMessage}
* @since Envoy Common v0.2-alpha
*/
public GroupMessage buildGroupMessage(Group group) {
var memberStatuses = new HashMap<Long, Message.MessageStatus>();
group.getContacts().forEach(user -> memberStatuses.put(user.getID(), MessageStatus.WAITING));
return buildGroupMessage(group, memberStatuses);
}
/**
* Creates an instance of {@link GroupMessage} with the previously supplied
* values. If a mandatory value is not set, a default value will be used
@ -116,22 +146,17 @@ public class MessageBuilder {
* <td>{@code text}</td>
* <td>{@code ""}</td>
* <tr>
* <tr>
* <td>{@code status}</td>
* <td>{@code MessageStatus.WAITING}</td>
* <tr>
* </table>
*
* @param group the {@link Group} that is used to fill the map of member
* statuses
* @param group the {@link Group} that is used to fill the map of
* member statuses
* @param memberStatuses the map of all current statuses
* @return a new instance of {@link GroupMessage}
* @since Envoy Common v0.2-alpha
* @since Envoy Common v0.1-beta
*/
public GroupMessage buildGroupMessage(Group group) {
if (group == null) throw new NullPointerException();
public GroupMessage buildGroupMessage(Group group, Map<Long, MessageStatus> memberStatuses) {
if (group == null || memberStatuses == null) throw new NullPointerException();
supplyDefaults();
var memberStatuses = new HashMap<Long, Message.MessageStatus>();
group.getMemberIDs().forEach(id -> memberStatuses.put(id, MessageStatus.WAITING));
return new GroupMessage(id, senderID, recipientID, creationDate, text, attachment, status, forwarded, memberStatuses);
}

View File

@ -1,5 +1,11 @@
package envoy.data;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
/**
* Represents a unique user with a unique, numeric ID, a name and a current
* {@link UserStatus}.<br>
@ -11,7 +17,16 @@ package envoy.data;
* @author Kai S. K. Engelbart
* @since Envoy Common v0.2-alpha
*/
public class User extends Contact {
public final class User extends Contact {
private UserStatus status;
/**
* Used to serialize contact list to a maximum depth of one
*/
private transient boolean serializeContacts = true;
private static final long serialVersionUID = 1L;
/**
* This enumeration defines all possible statuses a user can have.
@ -42,38 +57,50 @@ public class User extends Contact {
OFFLINE;
}
private UserStatus status;
private static final long serialVersionUID = 1L;
/**
* Initializes a {@link User}. <br>
* The {@link UserStatus} is set to {@link UserStatus#ONLINE}.
* No contacts are initialized.
*
* @param id unique ID
* @param name user name
* @since Envoy Common v0.2-alpha
*/
public User(long id, String name) {
super(id, name);
super(id, name, new HashSet<>());
status = UserStatus.ONLINE;
}
/**
* Initializes a {@link User}. <br>
* The {@link UserStatus} is set to {@link UserStatus#ONLINE}.
*
* @param id unique ID
* @param name user name
* @param contacts the contacts of this user
* @since Envoy Common v0.2-alpha
*/
public User(long id, String name, Set<Contact> contacts) {
super(id, name, contacts);
status = UserStatus.ONLINE;
}
/**
* Initializes a {@link User}.
*
* @param id unique ID
* @param name user name
* @param status the status of the user
* @param id unique ID
* @param name user name
* @param status the status of this user
* @param contacts the contacts of this user
* @since Envoy Common v0.2-alpha
*/
public User(long id, String name, UserStatus status) {
super(id, name);
public User(long id, String name, UserStatus status, Set<Contact> contacts) {
super(id, name, contacts);
this.status = status;
}
@Override
public String toString() { return String.format("User[id=%d,name=%s,status=%s]", getID(), getName(), status); }
public String toString() { return String.format("User[id=%d,name=%s,status=%s,contacts=%s]", getID(), getName(), status, getContacts()); }
/**
* @return the current status of this user
@ -86,4 +113,26 @@ public class User extends Contact {
* @since Envoy Common v0.2-alpha
*/
public void setStatus(UserStatus status) { this.status = status; }
private void readObject(ObjectInputStream inputStream) throws Exception {
inputStream.defaultReadObject();
var contacts = Contact.class.getDeclaredField("contacts");
contacts.setAccessible(true);
contacts.set(this, inputStream.readObject());
}
private void writeObject(ObjectOutputStream outputStream) throws Exception {
outputStream.defaultWriteObject();
if (serializeContacts) {
getContacts().stream().filter(User.class::isInstance).map(User.class::cast).forEach(user -> user.serializeContacts = false);
outputStream.writeObject(getContacts());
} else outputStream.writeObject(new ArrayList<>());
}
/**
* @param serializeContacts whether the contacts of this {@link User} should be
* serialized
* @since Envoy Common v0.1-beta
*/
public void serializeContacts(boolean serializeContacts) { this.serializeContacts = serializeContacts; }
}

View File

@ -1,5 +1,10 @@
package envoy.event;
import java.util.HashSet;
import java.util.Set;
import envoy.data.User;
/**
* This event creates a group with the given name.<br>
* <br>
@ -12,11 +17,26 @@ package envoy.event;
*/
public class GroupCreationEvent extends Event<String> {
private final Set<Long> initialMemberIDs;
private static final long serialVersionUID = 0L;
/**
* @param value the name of this group at creation time
* @param value the name of this group at creation time
* @param initialMemberIDs the IDs of all {@link User}s that should be group
* members from the beginning on (excluding the creator
* of this group)
* @since Envoy Common v0.1-beta
*/
public GroupCreationEvent(String value) { super(value); }
public GroupCreationEvent(String value, Set<Long> initialMemberIDs) {
super(value);
this.initialMemberIDs = (initialMemberIDs != null) ? initialMemberIDs : new HashSet<>();
}
/**
* @return the IDs of all {@link User}s that are members from the beginning
* (excluding the creator of this group)
* @since Envoy Common v0.1-beta
*/
public Set<Long> getInitialMemberIDs() { return initialMemberIDs; }
}

View File

@ -36,7 +36,7 @@ public class GroupResizeEvent extends Event<Long> {
*/
public GroupResizeEvent(User user, Group group, ElementOperation operation) {
super(user.getID());
if (group.getMemberIDs().contains(user.getID())) {
if (group.getContacts().contains(user)) {
if (operation.equals(ElementOperation.ADD)) throw new IllegalStateException(
"Cannot add " + user + " to group " + group.getID() + " because he is already a member of this group");
} else if (operation.equals(ElementOperation.REMOVE))

View File

@ -0,0 +1,35 @@
package envoy.data;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException;
import java.util.Set;
import org.junit.jupiter.api.Test;
import envoy.data.User.UserStatus;
import envoy.util.SerializationUtils;
/**
* Project: <strong>envoy-common</strong><br>
* File: <strong>UserTest.java</strong><br>
* Created: <strong>31 Mar 2020</strong><br>
*
* @author Leon Hofmeister
* @since Envoy Common v0.1-beta
*/
class UserTest {
@Test
void test() throws IOException, ClassNotFoundException {
User user2 = new User(2, "kai");
User user3 = new User(3, "ai");
User user4 = new User(4, "ki", Set.of(user2, user3));
User user5 = new User(5, "ka", Set.of(user2, user3, user4));
User user = new User(1, "maxi", UserStatus.AWAY, Set.of(user2, user3, user4, user5));
var serializedUser = SerializationUtils.writeToByteArray(user);
var deserializedUser = SerializationUtils.read(serializedUser, User.class);
assertEquals(user.getContacts(), deserializedUser.getContacts());
}
}