Reformatted EVERY SINGLE java file according to new formatter #102

Closed
delvh wants to merge 1 commits from new-formatter into develop
139 changed files with 2284 additions and 1596 deletions

View File

@ -7,8 +7,8 @@ import envoy.client.ui.Startup;
/**
* Triggers application startup.
* <p>
* To allow Maven shading, the main method has to be separated from the
* {@link Startup} class which extends {@link Application}.
* To allow Maven shading, the main method has to be separated from the {@link Startup} class which
* extends {@link Application}.
*
* @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta
@ -25,8 +25,7 @@ public final class Main {
/**
* Starts the application.
*
* @param args the command line arguments are processed by the
* client configuration
* @param args the command line arguments are processed by the client configuration
* @since Envoy Client v0.1-beta
*/
public static void main(String[] args) {

View File

@ -35,7 +35,9 @@ public final class Cache<T> implements Consumer<T>, Serializable {
}
@Override
public String toString() { return String.format("Cache[elements=" + elements + "]"); }
public String toString() {
return String.format("Cache[elements=" + elements + "]");
}
/**
* Sets the processor to which cached elements are relayed.
@ -52,7 +54,8 @@ public final class Cache<T> implements Consumer<T>, Serializable {
* @since Envoy Client v0.3-alpha
*/
public void relay() {
if (processor == null) throw new IllegalStateException("Processor is not defined");
if (processor == null)
throw new IllegalStateException("Processor is not defined");
elements.forEach(processor::accept);
elements.clear();
}
@ -62,5 +65,7 @@ public final class Cache<T> implements Consumer<T>, Serializable {
*
* @since Envoy Client v0.2-beta
*/
public void clear() { elements.clear(); }
public void clear() {
elements.clear();
}
}

View File

@ -4,8 +4,7 @@ import java.io.Serializable;
import java.util.*;
/**
* Stores a heterogeneous map of {@link Cache} objects with different type
* parameters.
* Stores a heterogeneous map of {@link Cache} objects with different type parameters.
*
* @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta
@ -24,7 +23,9 @@ public final class CacheMap implements Serializable {
* @param cache the cache to store
* @since Envoy Client v0.1-beta
*/
public <T> void put(Class<T> key, Cache<T> cache) { map.put(key, cache); }
public <T> void put(Class<T> key, Cache<T> cache) {
map.put(key, cache);
}
/**
* Returns a cache mapped by a class.
@ -34,7 +35,9 @@ public final class CacheMap implements Serializable {
* @return the cache
* @since Envoy Client v0.1-beta
*/
public <T> Cache<T> get(Class<T> key) { return (Cache<T>) map.get(key); }
public <T> Cache<T> get(Class<T> key) {
return (Cache<T>) map.get(key);
}
/**
* Returns a cache mapped by a class or any of its subclasses.
@ -64,5 +67,7 @@ public final class CacheMap implements Serializable {
*
* @since Envoy Client v0.2-beta
*/
public void clear() { map.values().forEach(Cache::clear); }
public void clear() {
map.values().forEach(Cache::clear);
}
}

View File

@ -5,14 +5,14 @@ import java.util.*;
import javafx.collections.*;
import envoy.client.net.WriteProxy;
import envoy.data.*;
import envoy.data.Message.MessageStatus;
import envoy.event.MessageStatusChange;
import envoy.client.net.WriteProxy;
/**
* Represents a chat between two {@link User}s
* as a list of {@link Message} objects.
* Represents a chat between two {@link User}s as a list of {@link Message} objects.
*
* @author Maximilian K&auml;fer
* @author Leon Hofmeister
@ -42,7 +42,9 @@ public class Chat implements Serializable {
* @param recipient the user who receives the messages
* @since Envoy Client v0.1-alpha
*/
public Chat(Contact recipient) { this.recipient = recipient; }
public Chat(Contact recipient) {
this.recipient = recipient;
}
private void readObject(ObjectInputStream stream) throws ClassNotFoundException, IOException {
stream.defaultReadObject();
@ -55,7 +57,10 @@ public class Chat implements Serializable {
}
@Override
public String toString() { return String.format("%s[recipient=%s,messages=%d]", getClass().getSimpleName(), recipient, messages.size()); }
public String toString() {
return String.format("%s[recipient=%s,messages=%d]", getClass().getSimpleName(), recipient,
messages.size());
}
/**
* Generates a hash code based on the recipient.
@ -63,7 +68,9 @@ public class Chat implements Serializable {
* @since Envoy Client v0.1-beta
*/
@Override
public int hashCode() { return Objects.hash(recipient); }
public int hashCode() {
return Objects.hash(recipient);
}
/**
* Tests equality to another object based on the recipient.
@ -72,39 +79,45 @@ public class Chat implements Serializable {
*/
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Chat)) return false;
if (this == obj)
return true;
if (!(obj instanceof Chat))
return false;
final var other = (Chat) obj;
return Objects.equals(recipient, other.recipient);
}
/**
* Sets the status of all chat messages received from the recipient to
* {@code READ} starting from the bottom and stopping once a read message is
* found.
* Sets the status of all chat messages received from the recipient to {@code READ} starting
* from the bottom and stopping once a read message is found.
*
* @param writeProxy the write proxy instance used to notify the server about
* the message status changes
* @param writeProxy the write proxy instance used to notify the server about the message status
* changes
* @since Envoy Client v0.3-alpha
*/
public void read(WriteProxy writeProxy) {
for (int i = messages.size() - 1; i >= 0; --i) {
final var m = messages.get(i);
if (m.getSenderID() == recipient.getID()) if (m.getStatus() == MessageStatus.READ) break;
else {
m.setStatus(MessageStatus.READ);
writeProxy.writeMessageStatusChange(new MessageStatusChange(m));
}
if (m.getSenderID() == recipient.getID())
if (m.getStatus() == MessageStatus.READ)
break;
else {
m.setStatus(MessageStatus.READ);
writeProxy.writeMessageStatusChange(new MessageStatusChange(m));
}
}
unreadAmount = 0;
}
/**
* @return {@code true} if the newest message received in the chat doesn't have
* the status {@code READ}
* @return {@code true} if the newest message received in the chat doesn't have the status
* {@code READ}
* @since Envoy Client v0.3-alpha
*/
public boolean isUnread() { return !messages.isEmpty() && messages.get(messages.size() - 1).getStatus() != MessageStatus.READ; }
public boolean isUnread() {
return !messages.isEmpty()
&& messages.get(messages.size() - 1).getStatus() != MessageStatus.READ;
}
/**
* Inserts a message at the correct place according to its creation date.
@ -128,14 +141,18 @@ public class Chat implements Serializable {
* @return whether the message has been found and removed
* @since Envoy Client v0.3-beta
*/
public boolean remove(long messageID) { return messages.removeIf(m -> m.getID() == messageID); }
public boolean remove(long messageID) {
return messages.removeIf(m -> m.getID() == messageID);
}
/**
* Increments the amount of unread messages.
*
* @since Envoy Client v0.1-beta
*/
public void incrementUnreadAmount() { ++unreadAmount; }
public void incrementUnreadAmount() {
++unreadAmount;
}
/**
* @return the amount of unread messages in this chat
@ -156,8 +173,7 @@ public class Chat implements Serializable {
public Contact getRecipient() { return recipient; }
/**
* @return the last known time a {@link envoy.event.IsTyping} event has been
* sent
* @return the last known time a {@link envoy.event.IsTyping} event has been sent
* @since Envoy Client v0.2-beta
*/
public long getLastWritingEvent() { return lastWritingEvent; }
@ -167,5 +183,7 @@ public class Chat implements Serializable {
*
* @since Envoy Client v0.2-beta
*/
public void lastWritingEventWasNow() { lastWritingEvent = System.currentTimeMillis(); }
public void lastWritingEventWasNow() {
lastWritingEvent = System.currentTimeMillis();
}
}

View File

@ -5,8 +5,8 @@ import static java.util.function.Function.identity;
import envoy.data.Config;
/**
* Implements a configuration specific to the Envoy Client with default values
* and convenience methods.
* Implements a configuration specific to the Envoy Client with default values and convenience
* methods.
*
* @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta
@ -20,7 +20,8 @@ public final class ClientConfig extends Config {
* @since Envoy Client v0.1-beta
*/
public static ClientConfig getInstance() {
if (config == null) config = new ClientConfig();
if (config == null)
config = new ClientConfig();
return config;
}
@ -47,5 +48,7 @@ public final class ClientConfig extends Config {
* @return the amount of minutes after which the local database should be saved
* @since Envoy Client v0.2-beta
*/
public Integer getLocalDBSaveInterval() { return (Integer) items.get("localDBSaveInterval").get(); }
public Integer getLocalDBSaveInterval() {
return (Integer) items.get("localDBSaveInterval").get();
}
}

View File

@ -36,7 +36,8 @@ public class Context {
* @since Envoy Client v0.2-beta
*/
public void initWriteProxy() {
if (localDB == null) throw new IllegalStateException("The LocalDB has to be initialized!");
if (localDB == null)
throw new IllegalStateException("The LocalDB has to be initialized!");
writeProxy = new WriteProxy(client, localDB);
}

View File

@ -2,14 +2,14 @@ package envoy.client.data;
import java.time.Instant;
import envoy.client.net.WriteProxy;
import envoy.data.*;
import envoy.data.Message.MessageStatus;
import envoy.event.GroupMessageStatusChange;
import envoy.client.net.WriteProxy;
/**
* Represents a chat between a user and a group
* as a list of messages.
* Represents a chat between a user and a group as a list of messages.
*
* @author Maximilian K&auml;fer
* @since Envoy Client v0.1-beta
@ -34,11 +34,16 @@ public final class GroupChat extends Chat {
public void read(WriteProxy writeProxy) {
for (int i = messages.size() - 1; i >= 0; --i) {
final GroupMessage gmsg = (GroupMessage) messages.get(i);
if (gmsg.getSenderID() != sender.getID()) if (gmsg.getMemberStatuses().get(sender.getID()) == MessageStatus.READ) break;
else {
gmsg.getMemberStatuses().replace(sender.getID(), MessageStatus.READ);
writeProxy.writeMessageStatusChange(new GroupMessageStatusChange(gmsg.getID(), MessageStatus.READ, Instant.now(), sender.getID()));
}
if (gmsg.getSenderID() != sender.getID())
if (gmsg.getMemberStatuses().get(sender.getID()) == MessageStatus.READ)
break;
else {
gmsg.getMemberStatuses().replace(sender.getID(), MessageStatus.READ);
writeProxy.writeMessageStatusChange(new GroupMessageStatusChange(gmsg.getID(),
MessageStatus.READ,
Instant.now(),
sender.getID()));
}
}
unreadAmount = 0;
}

View File

@ -10,20 +10,21 @@ import java.util.logging.*;
import javafx.application.Platform;
import javafx.collections.*;
import envoy.client.event.*;
import dev.kske.eventbus.Event;
import dev.kske.eventbus.EventBus;
import dev.kske.eventbus.EventListener;
import envoy.data.*;
import envoy.data.Message.MessageStatus;
import envoy.event.*;
import envoy.exception.EnvoyException;
import envoy.util.*;
import dev.kske.eventbus.Event;
import dev.kske.eventbus.EventBus;
import dev.kske.eventbus.EventListener;
import envoy.client.event.*;
/**
* Stores information about the current {@link User} and their {@link Chat}s.
* For message ID generation a {@link IDGenerator} is stored as well.
* Stores information about the current {@link User} and their {@link Chat}s. For message ID
* generation a {@link IDGenerator} is stored as well.
* <p>
* The managed objects are stored inside a folder in the local file system.
*
@ -68,8 +69,11 @@ public final class LocalDB implements EventListener {
EventBus.getInstance().registerListener(this);
// Ensure that the database directory exists
if (!dbDir.exists()) dbDir.mkdirs();
else if (!dbDir.isDirectory()) throw new IOException(String.format("LocalDBDir '%s' is not a directory!", dbDir.getAbsolutePath()));
if (!dbDir.exists())
dbDir.mkdirs();
else if (!dbDir.isDirectory())
throw new IOException(String.format("LocalDBDir '%s' is not a directory!",
dbDir.getAbsolutePath()));
// Lock the directory
lock();
@ -88,8 +92,7 @@ public final class LocalDB implements EventListener {
}
/**
* Ensured that only one Envoy instance is using this local database by creating
* a lock file.
* Ensured that only one Envoy instance is using this local database by creating a lock file.
* The lock file is deleted on application exit.
*
* @throws EnvoyException if the lock cannot by acquired
@ -98,17 +101,19 @@ public final class LocalDB implements EventListener {
private synchronized void lock() throws EnvoyException {
final var file = new File(dbDir, "instance.lock");
try {
final var fc = FileChannel.open(file.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
final var fc = FileChannel.open(file.toPath(), StandardOpenOption.CREATE,
StandardOpenOption.WRITE);
instanceLock = fc.tryLock();
if (instanceLock == null) throw new EnvoyException("Another Envoy instance is using this local database!");
if (instanceLock == null)
throw new EnvoyException("Another Envoy instance is using this local database!");
} catch (final IOException e) {
throw new EnvoyException("Could not create lock file!", e);
}
}
/**
* Loads the local user registry {@code users.db}, the id generator
* {@code id_gen.db} and last login file {@code last_login.db}.
* Loads the local user registry {@code users.db}, the id generator {@code id_gen.db} and last
* login file {@code last_login.db}.
*
* @since Envoy Client v0.2-beta
*/
@ -133,7 +138,8 @@ public final class LocalDB implements EventListener {
* @since Envoy Client v0.3-alpha
*/
public synchronized void loadUserData() throws ClassNotFoundException, IOException {
if (user == null) throw new IllegalStateException("Client user is null, cannot initialize user storage");
if (user == null)
throw new IllegalStateException("Client user is null, cannot initialize user storage");
userFile = new File(dbDir, user.getID() + ".db");
try (var in = new ObjectInputStream(new FileInputStream(userFile))) {
chats = FXCollections.observableList((List<Chat>) in.readObject());
@ -145,19 +151,22 @@ public final class LocalDB implements EventListener {
}
/**
* Synchronizes the contact list of the client user with the chat and user
* storage.
* Synchronizes the contact list of the client user with the chat and user storage.
*
* @since Envoy Client v0.1-beta
*/
private void synchronize() {
user.getContacts().stream().filter(u -> u instanceof User && !users.containsKey(u.getName())).forEach(u -> users.put(u.getName(), (User) u));
user.getContacts().stream()
.filter(u -> u instanceof User && !users.containsKey(u.getName()))
.forEach(u -> users.put(u.getName(), (User) u));
users.put(user.getName(), user);
// Synchronize user status data
for (final var contact : user.getContacts())
if (contact instanceof User)
getChat(contact.getID()).ifPresent(chat -> { ((User) chat.getRecipient()).setStatus(((User) contact).getStatus()); });
getChat(contact.getID()).ifPresent(chat -> {
((User) chat.getRecipient()).setStatus(((User) contact).getStatus());
});
// Create missing chats
user.getContacts()
@ -168,8 +177,8 @@ public final class LocalDB implements EventListener {
}
/**
* Initializes a timer that automatically saves this local database after a
* period of time specified in the settings.
* Initializes a timer that automatically saves this local database after a period of time
* specified in the settings.
*
* @since Envoy Client v0.2-beta
*/
@ -184,13 +193,15 @@ public final class LocalDB implements EventListener {
autoSaver.schedule(new TimerTask() {
@Override
public void run() { save(); }
public void run() {
save();
}
}, 2000, ClientConfig.getInstance().getLocalDBSaveInterval() * 60000);
}
/**
* Stores all users. If the client user is specified, their chats will be stored
* as well. The message id generator will also be saved if present.
* Stores all users. If the client user is specified, their chats will be stored as well. The
* message id generator will also be saved if present.
*
* @throws IOException if the saving process failed
* @since Envoy Client v0.3-alpha
@ -204,21 +215,31 @@ public final class LocalDB implements EventListener {
SerializationUtils.write(usersFile, users);
// Save user data and last sync time stamp
if (user != null) SerializationUtils
.write(userFile, new ArrayList<>(chats), cacheMap, Context.getInstance().getClient().isOnline() ? Instant.now() : lastSync);
if (user != null)
SerializationUtils
.write(userFile, new ArrayList<>(chats), cacheMap,
Context.getInstance().getClient().isOnline()
? Instant.now()
: lastSync);
// Save last login information
if (authToken != null) SerializationUtils.write(lastLoginFile, user, authToken);
if (authToken != null)
SerializationUtils.write(lastLoginFile, user, authToken);
// Save ID generator
if (hasIDGenerator()) SerializationUtils.write(idGeneratorFile, idGenerator);
if (hasIDGenerator())
SerializationUtils.write(idGeneratorFile, idGenerator);
} catch (final IOException e) {
EnvoyLog.getLogger(LocalDB.class).log(Level.SEVERE, "Unable to save local database: ", e);
EnvoyLog.getLogger(LocalDB.class).log(Level.SEVERE, "Unable to save local database: ",
e);
}
}
@Event(priority = 150)
private void onMessage(Message msg) { if (msg.getStatus() == MessageStatus.SENT) msg.nextStatus(); }
private void onMessage(Message msg) {
if (msg.getStatus() == MessageStatus.SENT)
msg.nextStatus();
}
@Event(priority = 150)
private void onGroupMessage(GroupMessage msg) {
@ -228,24 +249,32 @@ public final class LocalDB implements EventListener {
}
@Event(priority = 150)
private void onMessageStatusChange(MessageStatusChange evt) { getMessage(evt.getID()).ifPresent(msg -> msg.setStatus(evt.get())); }
private void onMessageStatusChange(MessageStatusChange evt) {
getMessage(evt.getID()).ifPresent(msg -> msg.setStatus(evt.get()));
}
@Event(priority = 150)
private void onGroupMessageStatusChange(GroupMessageStatusChange evt) {
this.<GroupMessage>getMessage(evt.getID()).ifPresent(msg -> msg.getMemberStatuses().replace(evt.getMemberID(), evt.get()));
this.<GroupMessage>getMessage(evt.getID())
.ifPresent(msg -> msg.getMemberStatuses().replace(evt.getMemberID(), evt.get()));
}
@Event(priority = 150)
private void onUserStatusChange(UserStatusChange evt) {
getChat(evt.getID()).map(Chat::getRecipient).map(User.class::cast).ifPresent(u -> u.setStatus(evt.get()));
getChat(evt.getID()).map(Chat::getRecipient).map(User.class::cast)
.ifPresent(u -> u.setStatus(evt.get()));
}
@Event(priority = 150)
private void onGroupResize(GroupResize evt) { getChat(evt.getGroupID()).map(Chat::getRecipient).map(Group.class::cast).ifPresent(evt::apply); }
private void onGroupResize(GroupResize evt) {
getChat(evt.getGroupID()).map(Chat::getRecipient).map(Group.class::cast)
.ifPresent(evt::apply);
}
@Event(priority = 150)
private void onNameChange(NameChange evt) {
chats.stream().map(Chat::getRecipient).filter(c -> c.getID() == evt.getID()).findAny().ifPresent(c -> c.setName(evt.get()));
chats.stream().map(Chat::getRecipient).filter(c -> c.getID() == evt.getID()).findAny()
.ifPresent(c -> c.setName(evt.get()));
}
/**
@ -255,7 +284,9 @@ public final class LocalDB implements EventListener {
* @since Envoy Client v0.2-beta
*/
@Event
private void onNewAuthToken(NewAuthToken evt) { authToken = evt.get(); }
private void onNewAuthToken(NewAuthToken evt) {
authToken = evt.get();
}
/**
* Deletes all associations to the current user.
@ -289,16 +320,18 @@ public final class LocalDB implements EventListener {
// once a message was removed
final var messageID = message.get();
for (final var chat : chats)
if (chat.remove(messageID)) break;
if (chat.remove(messageID))
break;
});
}
@Event(priority = 500)
private void onOwnStatusChange(OwnStatusChange statusChange) { user.setStatus(statusChange.get()); }
private void onOwnStatusChange(OwnStatusChange statusChange) {
user.setStatus(statusChange.get());
}
/**
* @return a {@code Map<String, User>} of all users stored locally with their
* user names as keys
* @return a {@code Map<String, User>} of all users stored locally with their user names as keys
* @since Envoy Client v0.2-alpha
*/
public Map<String, User> getUsers() { return users; }
@ -311,7 +344,8 @@ public final class LocalDB implements EventListener {
* @since Envoy Client v0.1-beta
*/
public <T extends Message> Optional<T> getMessage(long id) {
return (Optional<T>) chats.stream().map(Chat::getMessages).flatMap(List::stream).filter(m -> m.getID() == id).findAny();
return (Optional<T>) chats.stream().map(Chat::getMessages).flatMap(List::stream)
.filter(m -> m.getID() == id).findAny();
}
/**
@ -321,11 +355,12 @@ public final class LocalDB implements EventListener {
* @return an optional containing the chat
* @since Envoy Client v0.1-beta
*/
public Optional<Chat> getChat(long recipientID) { return chats.stream().filter(c -> c.getRecipient().getID() == recipientID).findAny(); }
public Optional<Chat> getChat(long recipientID) {
return chats.stream().filter(c -> c.getRecipient().getID() == recipientID).findAny();
}
/**
* @return all saved {@link Chat} objects that list the client user as the
* sender
* @return all saved {@link Chat} objects that list the client user as the sender
* @since Envoy Client v0.1-alpha
**/
public ObservableList<Chat> getChats() { return chats; }
@ -359,7 +394,9 @@ public final class LocalDB implements EventListener {
* @return {@code true} if an {@link IDGenerator} is present
* @since Envoy Client v0.3-alpha
*/
public boolean hasIDGenerator() { return idGenerator != null; }
public boolean hasIDGenerator() {
return idGenerator != null;
}
/**
* @return the cache map for messages and message status changes

View File

@ -5,16 +5,16 @@ import java.util.*;
import java.util.logging.Level;
import java.util.prefs.Preferences;
import envoy.client.event.EnvoyCloseEvent;
import envoy.util.*;
import dev.kske.eventbus.*;
import dev.kske.eventbus.EventListener;
import envoy.util.*;
import envoy.client.event.EnvoyCloseEvent;
/**
* Manages all application settings, which are different objects that can be
* changed during runtime and serialized them by using either the file system or
* the {@link Preferences} API.
* Manages all application settings, which are different objects that can be changed during runtime
* and serialized them by using either the file system or the {@link Preferences} API.
*
* @author Leon Hofmeister
* @author Maximilian K&auml;fer
@ -29,7 +29,8 @@ public final class Settings implements EventListener {
/**
* Settings are stored in this file.
*/
private static final File settingsFile = new File(ClientConfig.getInstance().getHomeDirectory(), "settings.ser");
private static final File settingsFile =
new File(ClientConfig.getInstance().getHomeDirectory(), "settings.ser");
/**
* Singleton instance of this class.
@ -37,8 +38,8 @@ public final class Settings implements EventListener {
private static Settings settings = new Settings();
/**
* The way to instantiate the settings. Is set to private to deny other
* instances of that object.
* The way to instantiate the settings. Is set to private to deny other instances of that
* object.
*
* @since Envoy Client v0.2-alpha
*/
@ -76,20 +77,32 @@ public final class Settings implements EventListener {
try {
SerializationUtils.write(settingsFile, items);
} catch (final IOException e) {
EnvoyLog.getLogger(Settings.class).log(Level.SEVERE, "Unable to save settings file: ", e);
EnvoyLog.getLogger(Settings.class).log(Level.SEVERE, "Unable to save settings file: ",
e);
}
}
private void supplementDefaults() {
items.putIfAbsent("enterToSend", new SettingsItem<>(true, "Enter to send", "Sends a message by pressing the enter key."));
items.putIfAbsent("hideOnClose", new SettingsItem<>(false, "Hide on close", "Hides the chat window when it is closed."));
items.putIfAbsent("currentTheme", new SettingsItem<>("dark", "Current Theme Name", "The name of the currently selected theme."));
items.putIfAbsent("enterToSend",
new SettingsItem<>(true, "Enter to send",
"Sends a message by pressing the enter key."));
items.putIfAbsent("hideOnClose",
new SettingsItem<>(false, "Hide on close",
"Hides the chat window when it is closed."));
items.putIfAbsent("currentTheme",
new SettingsItem<>("dark", "Current Theme Name",
"The name of the currently selected theme."));
items.putIfAbsent("downloadLocation",
new SettingsItem<>(new File(System.getProperty("user.home") + "/Downloads/"), "Download location",
"The location where files will be saved to"));
items.putIfAbsent("autoSaveDownloads", new SettingsItem<>(false, "Save without asking?", "Should downloads be saved without asking?"));
new SettingsItem<>(new File(System.getProperty("user.home")
+ "/Downloads/"),
"Download location",
"The location where files will be saved to"));
items.putIfAbsent("autoSaveDownloads",
new SettingsItem<>(false, "Save without asking?",
"Should downloads be saved without asking?"));
items.putIfAbsent("askForConfirmation",
new SettingsItem<>(true, "Ask for confirmation", "Will ask for confirmation before doing certain things"));
new SettingsItem<>(true, "Ask for confirmation",
"Will ask for confirmation before doing certain things"));
}
/**
@ -104,7 +117,9 @@ public final class Settings implements EventListener {
* @param themeName the name to set
* @since Envoy Client v0.2-alpha
*/
public void setCurrentTheme(String themeName) { ((SettingsItem<String>) items.get("currentTheme")).set(themeName); }
public void setCurrentTheme(String themeName) {
((SettingsItem<String>) items.get("currentTheme")).set(themeName);
}
/**
* @return true if the currently used theme is one of the default themes
@ -116,9 +131,8 @@ public final class Settings implements EventListener {
}
/**
* @return {@code true}, if pressing the {@code Enter} key suffices to send a
* message. Otherwise it has to be pressed in conjunction with the
* {@code Control} key.
* @return {@code true}, if pressing the {@code Enter} key suffices to send a message. Otherwise
* it has to be pressed in conjunction with the {@code Control} key.
* @since Envoy Client v0.2-alpha
*/
public Boolean isEnterToSend() { return (Boolean) items.get("enterToSend").get(); }
@ -126,26 +140,27 @@ public final class Settings implements EventListener {
/**
* Changes the keystrokes performed by the user to send a message.
*
* @param enterToSend If set to {@code true} a message can be sent by pressing
* the {@code Enter} key. Otherwise it has to be pressed in
* conjunction with the {@code Control} key.
* @param enterToSend If set to {@code true} a message can be sent by pressing the {@code Enter}
* key. Otherwise it has to be pressed in conjunction with the
* {@code Control} key.
* @since Envoy Client v0.2-alpha
*/
public void setEnterToSend(boolean enterToSend) { ((SettingsItem<Boolean>) items.get("enterToSend")).set(enterToSend); }
public void setEnterToSend(boolean enterToSend) {
((SettingsItem<Boolean>) items.get("enterToSend")).set(enterToSend);
}
/**
* @return whether Envoy will prompt a dialogue before saving an
* {@link envoy.data.Attachment}
* @return whether Envoy will prompt a dialogue before saving an {@link envoy.data.Attachment}
* @since Envoy Client v0.2-beta
*/
public Boolean isDownloadSavedWithoutAsking() { return (Boolean) items.get("autoSaveDownloads").get(); }
public Boolean isDownloadSavedWithoutAsking() {
return (Boolean) items.get("autoSaveDownloads").get();
}
/**
* Sets whether Envoy will prompt a dialogue before saving an
* {@link envoy.data.Attachment}.
* Sets whether Envoy will prompt a dialogue before saving an {@link envoy.data.Attachment}.
*
* @param autosaveDownload whether a download should be saved without asking
* before
* @param autosaveDownload whether a download should be saved without asking before
* @since Envoy Client v0.2-beta
*/
public void setDownloadSavedWithoutAsking(boolean autosaveDownload) {
@ -164,7 +179,9 @@ public final class Settings implements EventListener {
* @param downloadLocation the path to set
* @since Envoy Client v0.2-beta
*/
public void setDownloadLocation(File downloadLocation) { ((SettingsItem<File>) items.get("downloadLocation")).set(downloadLocation); }
public void setDownloadLocation(File downloadLocation) {
((SettingsItem<File>) items.get("downloadLocation")).set(downloadLocation);
}
/**
* @return the current on close mode.
@ -178,21 +195,24 @@ public final class Settings implements EventListener {
* @param hideOnClose whether the application should be minimized on close
* @since Envoy Client v0.3-alpha
*/
public void setHideOnClose(boolean hideOnClose) { ((SettingsItem<Boolean>) items.get("hideOnClose")).set(hideOnClose); }
public void setHideOnClose(boolean hideOnClose) {
((SettingsItem<Boolean>) items.get("hideOnClose")).set(hideOnClose);
}
/**
* @return whether a confirmation dialog should be displayed before certain
* actions
* @return whether a confirmation dialog should be displayed before certain actions
* @since Envoy Client v0.2-alpha
*/
public Boolean isAskForConfirmation() { return (Boolean) items.get("askForConfirmation").get(); }
public Boolean isAskForConfirmation() {
return (Boolean) items.get("askForConfirmation").get();
}
/**
* Changes the behavior of calling certain functionality by displaying a
* confirmation dialog before executing it.
* Changes the behavior of calling certain functionality by displaying a confirmation dialog
* before executing it.
*
* @param askForConfirmation whether confirmation dialogs should be displayed
* before certain actions
* @param askForConfirmation whether confirmation dialogs should be displayed before certain
* actions
* @since Envoy Client v0.2-alpha
*/
public void setAskForConfirmation(boolean askForConfirmation) {

View File

@ -6,8 +6,7 @@ import java.util.function.Consumer;
import javax.swing.JComponent;
/**
* Encapsulates a persistent value that is directly or indirectly mutable by the
* user.
* Encapsulates a persistent value that is directly or indirectly mutable by the user.
*
* @param <T> the type of this {@link SettingsItem}'s value
* @author Kai S. K. Engelbart
@ -23,9 +22,8 @@ public final class SettingsItem<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Initializes a {@link SettingsItem}. The default value's class will be mapped
* to a {@link JComponent} that can be used to display this {@link SettingsItem}
* to the user.
* Initializes a {@link SettingsItem}. The default value's class will be mapped to a
* {@link JComponent} that can be used to display this {@link SettingsItem} to the user.
*
* @param value the default value
* @param userFriendlyName the user friendly name (short)
@ -42,17 +40,20 @@ public final class SettingsItem<T> implements Serializable {
* @return the value
* @since Envoy Client v0.3-alpha
*/
public T get() { return value; }
public T get() {
return value;
}
/**
* Changes the value of this {@link SettingsItem}. If a {@code ChangeHandler} if
* defined, it will be invoked with this value.
* Changes the value of this {@link SettingsItem}. If a {@code ChangeHandler} if defined, it
* will be invoked with this value.
*
* @param value the value to set
* @since Envoy Client v0.3-alpha
*/
public void set(T value) {
if (changeHandler != null && value != this.value) changeHandler.accept(value);
if (changeHandler != null && value != this.value)
changeHandler.accept(value);
this.value = value;
}
@ -66,7 +67,9 @@ public final class SettingsItem<T> implements Serializable {
* @param userFriendlyName the userFriendlyName to set
* @since Envoy Client v0.3-alpha
*/
public void setUserFriendlyName(String userFriendlyName) { this.userFriendlyName = userFriendlyName; }
public void setUserFriendlyName(String userFriendlyName) {
this.userFriendlyName = userFriendlyName;
}
/**
* @return the description
@ -81,9 +84,8 @@ public final class SettingsItem<T> implements Serializable {
public void setDescription(String description) { this.description = description; }
/**
* Sets a {@code ChangeHandler} for this {@link SettingsItem}. It will be
* invoked with the current value once during the registration and every time
* when the value changes.
* Sets a {@code ChangeHandler} for this {@link SettingsItem}. It will be invoked with the
* current value once during the registration and every time when the value changes.
*
* @param changeHandler the changeHandler to set
* @since Envoy Client v0.3-alpha

View File

@ -22,7 +22,9 @@ public final class AudioPlayer {
*
* @since Envoy Client v0.1-beta
*/
public AudioPlayer() { this(AudioRecorder.DEFAULT_AUDIO_FORMAT); }
public AudioPlayer() {
this(AudioRecorder.DEFAULT_AUDIO_FORMAT);
}
/**
* Initializes the player with a given audio format.

View File

@ -20,7 +20,8 @@ public final class AudioRecorder {
*
* @since Envoy Client v0.1-beta
*/
public static final AudioFormat DEFAULT_AUDIO_FORMAT = new AudioFormat(16000, 16, 1, true, false);
public static final AudioFormat DEFAULT_AUDIO_FORMAT =
new AudioFormat(16000, 16, 1, true, false);
/**
* The format in which audio files will be saved.
@ -38,7 +39,9 @@ public final class AudioRecorder {
*
* @since Envoy Client v0.1-beta
*/
public AudioRecorder() { this(DEFAULT_AUDIO_FORMAT); }
public AudioRecorder() {
this(DEFAULT_AUDIO_FORMAT);
}
/**
* Initializes the recorder with a given audio format.

View File

@ -3,8 +3,7 @@ package envoy.client.data.commands;
import java.util.List;
/**
* This interface defines an action that should be performed when a system
* command gets called.
* This interface defines an action that should be performed when a system command gets called.
*
* @author Leon Hofmeister
* @since Envoy Client v0.2-beta
@ -12,11 +11,9 @@ import java.util.List;
public interface Callable {
/**
* Performs the instance specific action when a {@link SystemCommand} has been
* called.
* Performs the instance specific action when a {@link SystemCommand} has been called.
*
* @param arguments the arguments that should be passed to the
* {@link SystemCommand}
* @param arguments the arguments that should be passed to the {@link SystemCommand}
* @since Envoy Client v0.2-beta
*/
void call(List<String> arguments);

View File

@ -4,15 +4,12 @@ import java.util.*;
import java.util.function.Consumer;
/**
* This class is the base class of all {@code SystemCommands} and contains an
* action and a number of arguments that should be used as input for this
* function.
* No {@code SystemCommand} can return anything.
* Every {@code SystemCommand} must have as argument type {@code List<String>}
* so that the words following the indicator String can be used as input of the
* function. This approach has one limitation:<br>
* <b>Order matters!</b> Changing the order of arguments will likely result in
* unexpected behavior.
* This class is the base class of all {@code SystemCommands} and contains an action and a number of
* arguments that should be used as input for this function. No {@code SystemCommand} can return
* anything. Every {@code SystemCommand} must have as argument type {@code List<String>} so that the
* words following the indicator String can be used as input of the function. This approach has one
* limitation:<br>
* <b>Order matters!</b> Changing the order of arguments will likely result in unexpected behavior.
*
* @author Leon Hofmeister
* @since Envoy Client v0.2-beta
@ -28,8 +25,8 @@ public final class SystemCommand implements Callable {
/**
* This function takes a {@code List<String>} as argument because automatically
* {@code SystemCommand#numberOfArguments} words following the necessary command
* will be put into this list.
* {@code SystemCommand#numberOfArguments} words following the necessary command will be put
* into this list.
*
* @see String#split(String)
*/
@ -48,7 +45,8 @@ public final class SystemCommand implements Callable {
* @param description the description of this {@code SystemCommand}
* @since Envoy Client v0.2-beta
*/
public SystemCommand(Consumer<List<String>> action, int numberOfArguments, List<String> defaults, String description) {
public SystemCommand(Consumer<List<String>> action, int numberOfArguments,
List<String> defaults, String description) {
this.numberOfArguments = numberOfArguments;
this.action = action;
this.defaults = defaults == null ? new ArrayList<>() : defaults;
@ -92,20 +90,27 @@ public final class SystemCommand implements Callable {
public List<String> getDefaults() { return defaults; }
@Override
public int hashCode() { return Objects.hash(action); }
public int hashCode() {
return Objects.hash(action);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final var other = (SystemCommand) obj;
return Objects.equals(action, other.action);
}
@Override
public String toString() {
return "SystemCommand [relevance=" + relevance + ", numberOfArguments=" + numberOfArguments + ", "
+ (description != null ? "description=" + description + ", " : "") + (defaults != null ? "defaults=" + defaults : "") + "]";
return "SystemCommand [relevance=" + relevance + ", numberOfArguments=" + numberOfArguments
+ ", "
+ (description != null ? "description=" + description + ", " : "")
+ (defaults != null ? "defaults=" + defaults : "") + "]";
}
}

View File

@ -20,18 +20,21 @@ public final class SystemCommandBuilder {
private final SystemCommandMap commandsMap;
/**
* Creates a new {@code SystemCommandsBuilder} without underlying
* {@link SystemCommandMap}.
* Creates a new {@code SystemCommandsBuilder} without underlying {@link SystemCommandMap}.
*
* @since Envoy Client v0.2-beta
*/
public SystemCommandBuilder() { this(null); }
public SystemCommandBuilder() {
this(null);
}
/**
* @param commandsMap the map to use when calling build (optional)
* @since Envoy Client v0.2-beta
*/
public SystemCommandBuilder(SystemCommandMap commandsMap) { this.commandsMap = commandsMap; }
public SystemCommandBuilder(SystemCommandMap commandsMap) {
this.commandsMap = commandsMap;
}
/**
* @param numberOfArguments the numberOfArguments to set
@ -104,12 +107,14 @@ public final class SystemCommandBuilder {
* @return the built {@code SystemCommand}
* @since Envoy Client v0.2-beta
*/
public SystemCommand build() { return build(true); }
public SystemCommand build() {
return build(true);
}
/**
* Builds a {@code SystemCommand} based upon the previously entered data.<br>
* {@code SystemCommand#numberOfArguments} will be set to 0, regardless of the
* previous value.<br>
* {@code SystemCommand#numberOfArguments} will be set to 0, regardless of the previous
* value.<br>
* At the end, this {@code SystemCommandBuilder} will be reset.
*
* @return the built {@code SystemCommand}
@ -122,8 +127,8 @@ public final class SystemCommandBuilder {
/**
* Builds a {@code SystemCommand} based upon the previously entered data.<br>
* {@code SystemCommand#numberOfArguments} will be set to use the rest of the
* string as argument, regardless of the previous value.<br>
* {@code SystemCommand#numberOfArguments} will be set to use the rest of the string as
* argument, regardless of the previous value.<br>
* At the end, this {@code SystemCommandBuilder} will be reset.
*
* @return the built {@code SystemCommand}
@ -136,27 +141,25 @@ public final class SystemCommandBuilder {
/**
* Builds a {@code SystemCommand} based upon the previously entered data.<br>
* Automatically adds the built object to the given map.
* At the end, this {@code SystemCommandBuilder} <b>can</b> be reset but must
* not be.
* Automatically adds the built object to the given map. At the end, this
* {@code SystemCommandBuilder} <b>can</b> be reset but must not be.
*
* @param reset whether this {@code SystemCommandBuilder} should be reset
* afterwards.<br>
* This can be useful if another command wants to execute something
* similar
* @param reset whether this {@code SystemCommandBuilder} should be reset afterwards.<br>
* This can be useful if another command wants to execute something similar
* @return the built {@code SystemCommand}
* @since Envoy Client v0.2-beta
*/
public SystemCommand build(boolean reset) {
final var sc = new SystemCommand(action, numberOfArguments, defaults, description);
sc.setRelevance(relevance);
if (reset) reset();
if (reset)
reset();
return sc;
}
/**
* Builds a {@code SystemCommand} based upon the previously entered data.
* Automatically adds the built object to the given map.
* Builds a {@code SystemCommand} based upon the previously entered data. Automatically adds the
* built object to the given map.
*
* @param command the command under which to store the SystemCommand in the
* {@link SystemCommandMap}
@ -164,13 +167,14 @@ public final class SystemCommandBuilder {
* @throws NullPointerException if no map has been assigned to this builder
* @since Envoy Client v0.2-beta
*/
public SystemCommand build(String command) { return build(command, true); }
public SystemCommand build(String command) {
return build(command, true);
}
/**
* Builds a {@code SystemCommand} based upon the previously entered data.<br>
* Automatically adds the built object to the given map.
* {@code SystemCommand#numberOfArguments} will be set to 0, regardless of the
* previous value.<br>
* Automatically adds the built object to the given map. {@code SystemCommand#numberOfArguments}
* will be set to 0, regardless of the previous value.<br>
* At the end, this {@code SystemCommandBuilder} will be reset.
*
* @param command the command under which to store the SystemCommand in the
@ -186,9 +190,8 @@ public final class SystemCommandBuilder {
/**
* Builds a {@code SystemCommand} based upon the previously entered data.<br>
* Automatically adds the built object to the given map.
* {@code SystemCommand#numberOfArguments} will be set to use the rest of the
* string as argument, regardless of the previous value.<br>
* Automatically adds the built object to the given map. {@code SystemCommand#numberOfArguments}
* will be set to use the rest of the string as argument, regardless of the previous value.<br>
* At the end, this {@code SystemCommandBuilder} will be reset.
*
* @param command the command under which to store the SystemCommand in the
@ -204,17 +207,13 @@ public final class SystemCommandBuilder {
/**
* Builds a {@code SystemCommand} based upon the previously entered data.<br>
* Automatically adds the built object to the given map.
* At the end, this {@code SystemCommandBuilder} <b>can</b> be reset but must
* not be.
* Automatically adds the built object to the given map. At the end, this
* {@code SystemCommandBuilder} <b>can</b> be reset but must not be.
*
* @param command the command under which to store the SystemCommand in the
* {@link SystemCommandMap}
* @param reset whether this {@code SystemCommandBuilder} should be reset
* afterwards.<br>
* This can be useful if another command wants to execute
* something
* similar
* @param reset whether this {@code SystemCommandBuilder} should be reset afterwards.<br>
* This can be useful if another command wants to execute something similar
* @return the built {@code SystemCommand}
* @throws NullPointerException if no map has been assigned to this builder
* @since Envoy Client v0.2-beta
@ -222,9 +221,12 @@ public final class SystemCommandBuilder {
public SystemCommand build(String command, boolean reset) {
final var sc = new SystemCommand(action, numberOfArguments, defaults, description);
sc.setRelevance(relevance);
if (commandsMap != null) commandsMap.add(command, sc);
else throw new NullPointerException("No map in SystemCommandsBuilder present");
if (reset) reset();
if (commandsMap != null)
commandsMap.add(command, sc);
else
throw new NullPointerException("No map in SystemCommandsBuilder present");
if (reset)
reset();
return sc;
}
}

View File

@ -14,11 +14,9 @@ import javafx.scene.control.Alert.AlertType;
import envoy.util.EnvoyLog;
/**
* Stores all {@link SystemCommand}s used.
* SystemCommands can be called using an activator char and the text that needs
* to be present behind the activator.
* Additionally offers the option to request recommendations for a partial input
* String.
* Stores all {@link SystemCommand}s used. SystemCommands can be called using an activator char and
* the text that needs to be present behind the activator. Additionally offers the option to request
* recommendations for a partial input String.
*
* @author Leon Hofmeister
* @since Envoy Client v0.2-beta
@ -27,96 +25,99 @@ public final class SystemCommandMap {
private final Character activator;
private final Map<String, SystemCommand> systemCommands = new HashMap<>();
private final Pattern commandPattern = Pattern.compile("^[a-zA-Z0-9_:!/\\(\\)\\?\\.\\,\\;\\-]+$");
private final Pattern commandPattern =
Pattern.compile("^[a-zA-Z0-9_:!/\\(\\)\\?\\.\\,\\;\\-]+$");
private static final Logger logger = EnvoyLog.getLogger(SystemCommandMap.class);
/**
* Creates a new {@code SystemCommandMap} with the given char as activator.
* If this Character is null, any text used as input will be treated as a system
* command.
* Creates a new {@code SystemCommandMap} with the given char as activator. If this Character is
* null, any text used as input will be treated as a system command.
*
* @param activator the char to use as activator for commands
* @since Envoy Client v0.3-beta
*/
public SystemCommandMap(Character activator) { this.activator = activator; }
public SystemCommandMap(Character activator) {
this.activator = activator;
}
/**
* Creates a new {@code SystemCommandMap} with '/' as activator.
*
* @since Envoy Client v0.3-beta
*/
public SystemCommandMap() { activator = '/'; }
public SystemCommandMap() {
activator = '/';
}
/**
* Adds a new command to the map if the command name is valid.
*
* @param command the input string to execute the
* given action
* @param systemCommand the command to add - can be built using
* {@link SystemCommandBuilder}
* @param command the input string to execute the given action
* @param systemCommand the command to add - can be built using {@link SystemCommandBuilder}
* @see SystemCommandMap#isValidKey(String)
* @since Envoy Client v0.2-beta
*/
public void add(String command, SystemCommand systemCommand) {
if (isValidKey(command)) systemCommands.put(command.toLowerCase(), systemCommand);
if (isValidKey(command))
systemCommands.put(command.toLowerCase(), systemCommand);
}
/**
* This method checks if the input String is a key in the map and returns the
* wrapped System command if present.
* This method checks if the input String is a key in the map and returns the wrapped System
* command if present.
* <p>
* Usage example:<br>
* {@code SystemCommandMap systemCommands = new SystemCommandMap('*');}<br>
* {@code systemCommands.add("example", new SystemCommand(text -> {}, 1, null,
* ""));}<br>
* {@code systemCommands.add("example", new SystemCommand(text -> {}, 1, null, ""));}<br>
* {@code ....}<br>
* user input: {@code "*example xyz ..."}<br>
* {@code systemCommands.get("example xyz ...")} or
* {@code systemCommands.get("*example xyz ...")}
* result: {@code Optional<SystemCommand>.get() != null}
* {@code systemCommands.get("*example xyz ...")} result:
* {@code Optional<SystemCommand>.get() != null}
*
* @param input the input string given by the user
* @return the wrapped system command, if present
* @since Envoy Client v0.2-beta
*/
public Optional<SystemCommand> get(String input) { return Optional.ofNullable(systemCommands.get(getCommand(input.toLowerCase()))); }
public Optional<SystemCommand> get(String input) {
return Optional.ofNullable(systemCommands.get(getCommand(input.toLowerCase())));
}
/**
* This method ensures that the activator of a {@link SystemCommand} is
* stripped.<br>
* It only checks the word beginning from the first non-blank position in the
* input.
* It returns the command as (most likely) entered as key in the map for the
* first word of the text.<br>
* This method ensures that the activator of a {@link SystemCommand} is stripped.<br>
* It only checks the word beginning from the first non-blank position in the input. It returns
* the command as (most likely) entered as key in the map for the first word of the text.<br>
* Activators in the middle of the word will be disregarded.
*
* @param raw the input
* @return the command as entered in the map
* @since Envoy Client v0.2-beta
* @apiNote this method will (most likely) not return anything useful if
* whatever is entered after the activator is not a system command.
* Only exception: for recommendation purposes.
* @apiNote this method will (most likely) not return anything useful if whatever is entered
* after the activator is not a system command. Only exception: for recommendation
* purposes.
*/
public String getCommand(String raw) {
final var trimmed = raw.stripLeading();
// Entering only the activator should not throw an error
if (trimmed.length() == 1 && activator != null && activator.equals(trimmed.charAt(0))) return "";
if (trimmed.length() == 1 && activator != null && activator.equals(trimmed.charAt(0)))
return "";
else {
final var index = trimmed.indexOf(' ');
return trimmed.substring(activator != null && activator.equals(trimmed.charAt(0)) ? 1 : 0, index < 1 ? trimmed.length() : index);
return trimmed.substring(activator != null && activator.equals(trimmed.charAt(0)) ? 1
: 0, index < 1 ? trimmed.length() : index);
}
}
/**
* Examines whether a key can be put in the map and logs it with
* {@code Level.WARNING} if that key violates API constrictions.<br>
* Examines whether a key can be put in the map and logs it with {@code Level.WARNING} if that
* key violates API constrictions.<br>
* (allowed chars are <b>a-zA-Z0-9_:!/()?.,;-</b>)
* <p>
* The approach to not throw an exception was taken so that an ugly try-catch
* block for every addition to the system commands map could be avoided, an
* error that should only occur during implementation and not in production.
* The approach to not throw an exception was taken so that an ugly try-catch block for every
* addition to the system commands map could be avoided, an error that should only occur during
* implementation and not in production.
*
* @param command the key to examine
* @return whether this key can be used in the map
@ -124,17 +125,18 @@ public final class SystemCommandMap {
*/
public boolean isValidKey(String command) {
final var valid = commandPattern.matcher(command).matches();
if (!valid) logger.log(Level.WARNING,
if (!valid)
logger.log(Level.WARNING,
"The command \"" + command
+ "\" is not valid. As it might cause problems when executed, it will not be entered into the map. Only the characters "
+ commandPattern + "are allowed");
+ "\" is not valid. As it might cause problems when executed, it will not be entered into the map. Only the characters "
+ commandPattern + "are allowed");
return valid;
}
/**
* Takes a 'raw' string (the whole input) and checks if the activator is the
* first visible character and then checks if a command is present after that
* activator. If that is the case, it will be executed.
* Takes a 'raw' string (the whole input) and checks if the activator is the first visible
* character and then checks if a command is present after that activator. If that is the case,
* it will be executed.
*
* @param raw the raw input string
* @return whether a command could be found and successfully executed
@ -144,24 +146,26 @@ public final class SystemCommandMap {
// possibly a command was detected and could be executed
final var raw2 = raw.stripLeading();
final var commandFound = activator == null || raw2.startsWith(activator.toString()) ? executeAvailableCommand(raw2) : false;
final var commandFound = activator == null || raw2.startsWith(activator.toString())
? executeAvailableCommand(raw2)
: false;
// the command was executed successfully - no further checking needed
if (commandFound) logger.log(Level.FINE, "executed system command " + getCommand(raw2));
if (commandFound)
logger.log(Level.FINE, "executed system command " + getCommand(raw2));
return commandFound;
}
/**
* Retrieves the recommendations based on the current input entered.<br>
* The first word is used for the recommendations and
* it does not matter if the activator is at its beginning or not.<br>
* The first word is used for the recommendations and it does not matter if the activator is at
* its beginning or not.<br>
* If recommendations are present, the given function will be executed on the
* recommendations.<br>
* Otherwise nothing will be done.<br>
*
* @param input the input string
* @param action the action that should be taken for the recommendations, if any
* are present
* @param action the action that should be taken for the recommendations, if any are present
* @since Envoy Client v0.2-beta
*/
public void requestRecommendations(String input, Consumer<Set<String>> action) {
@ -169,27 +173,28 @@ public final class SystemCommandMap {
// Get the expected commands
final var recommendations = recommendCommands(partialCommand);
if (recommendations.isEmpty()) return;
if (recommendations.isEmpty())
return;
// Execute the given action
else action.accept(recommendations);
else
action.accept(recommendations);
}
/**
* This method checks if the input String is a key in the map and executes the
* wrapped System command if present.
* This method checks if the input String is a key in the map and executes the wrapped System
* command if present.
* <p>
* Usage example:<br>
* {@code SystemCommandMap systemCommands = new SystemCommandMap('*');}<br>
* {@code Button button = new Button();}<br>
* {@code systemCommands.add("example", new SystemCommand(text ->
* {button.setText(text.get(0))}, 1, null,
* ""));}<br>
* {@code systemCommands.add("example", new SystemCommand(text -> {button.setText(text.get(0))},
* 1, null, ""));}<br>
* {@code ....}<br>
* user input: {@code "*example xyz ..."}<br>
* {@code systemCommands.executeIfPresent("example xyz ...")} or
* {@code systemCommands.executeIfPresent("*example xyz ...")}
* result: {@code button.getText()=="xyz"}
* {@code systemCommands.executeIfPresent("*example xyz ...")} result:
* {@code button.getText()=="xyz"}
*
* @param input the input string given by the user
* @return whether a command could be found and successfully executed
@ -211,9 +216,9 @@ public final class SystemCommandMap {
systemCommand.call(arguments);
} catch (final NumberFormatException e) {
logger.log(Level.INFO,
String.format(
"System command %s could not be performed correctly because the user is a dumbass and could not write a parseable number.",
command));
String.format(
"System command %s could not be performed correctly because the user is a dumbass and could not write a parseable number.",
command));
Platform.runLater(() -> {
final var alert = new Alert(AlertType.ERROR);
alert.setContentText("Please enter a readable number as argument.");
@ -224,7 +229,8 @@ public final class SystemCommandMap {
logger.log(Level.WARNING, "System command " + command + " threw an exception: ", e);
Platform.runLater(() -> {
final var alert = new Alert(AlertType.ERROR);
alert.setContentText("Could not execute system command: Internal error. Please insult the responsible programmer.");
alert.setContentText(
"Could not execute system command: Internal error. Please insult the responsible programmer.");
alert.showAndWait();
});
commandExecuted.set(false);
@ -245,7 +251,8 @@ public final class SystemCommandMap {
// no more arguments follow after the command (e.g. text = "/DABR")
final var indexOfSpace = input.indexOf(" ");
if (indexOfSpace < 0) return supplementDefaults(new String[] {}, systemCommand);
if (indexOfSpace < 0)
return supplementDefaults(new String[] {}, systemCommand);
// the arguments behind a system command
final var remainingString = input.substring(indexOfSpace + 1);
@ -253,15 +260,17 @@ public final class SystemCommandMap {
// splitting those arguments and supplying default values
final var textArguments = remainingString.split(" ", -1);
final var originalArguments = numberOfArguments >= 0 ? Arrays.copyOfRange(textArguments, 0, numberOfArguments) : textArguments;
final var originalArguments =
numberOfArguments >= 0 ? Arrays.copyOfRange(textArguments, 0, numberOfArguments)
: textArguments;
final var arguments = supplementDefaults(originalArguments, systemCommand);
return arguments;
}
/**
* Recommends commands based upon the currently entered input.<br>
* In the current implementation, all that gets checked is whether a key
* contains this input. This might be updated later on.
* In the current implementation, all that gets checked is whether a key contains this input.
* This might be updated later on.
*
* @param partialCommand the partially entered command
* @return a set of all commands that match this input
@ -274,36 +283,43 @@ public final class SystemCommandMap {
return systemCommands.keySet()
.stream()
.filter(command -> command.contains(partialCommand))
.sorted((command1, command2) -> Integer.compare(systemCommands.get(command1).getRelevance(), systemCommands.get(command2).getRelevance()))
.sorted((command1,
command2) -> Integer.compare(systemCommands.get(command1)
.getRelevance(),
systemCommands.get(command2)
.getRelevance()))
.collect(Collectors.toSet());
}
/**
* Supplies the default values for arguments if none are present in the text for
* any argument. <br>
* Supplies the default values for arguments if none are present in the text for any argument.
* <br>
*
* @param textArguments the arguments that were parsed from the text
* @param toEvaluate the system command whose default values should be used
* @return the final argument list
* @since Envoy Client v0.2-beta
* @apiNote this method will insert an empty String if the size of the list
* given to the {@code SystemCommand} is smaller than its argument
* counter and no more text arguments could be found.
* @apiNote this method will insert an empty String if the size of the list given to the
* {@code SystemCommand} is smaller than its argument counter and no more text
* arguments could be found.
*/
private List<String> supplementDefaults(String[] textArguments, SystemCommand toEvaluate) {
final var defaults = toEvaluate.getDefaults();
final var numberOfArguments = toEvaluate.getNumberOfArguments();
final List<String> result = new ArrayList<>();
if (toEvaluate.getNumberOfArguments() > 0) for (var index = 0; index < numberOfArguments; index++) {
String textArg = null;
if (index < textArguments.length) textArg = textArguments[index];
if (toEvaluate.getNumberOfArguments() > 0)
for (var index = 0; index < numberOfArguments; index++) {
String textArg = null;
if (index < textArguments.length)
textArg = textArguments[index];
// Set the argument at position index to the current argument of the text, if it
// is present. Otherwise the default for that argument will be taken if present.
// In the worst case, an empty String will be used.
result.add(!(textArg == null) && !textArg.isBlank() ? textArg : index < defaults.size() ? defaults.get(index) : "");
}
// Set the argument at position index to the current argument of the text, if it
// is present. Otherwise the default for that argument will be taken if present.
// In the worst case, an empty String will be used.
result.add(!(textArg == null) && !textArg.isBlank() ? textArg
: index < defaults.size() ? defaults.get(index) : "");
}
return result;
}

View File

@ -28,17 +28,20 @@ public class EnvoyShortcutConfig {
// Add the option to exit with "Control" + "Q" or "Alt" + "F4" as offered by
// some desktop environments
instance.add(new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN), ShutdownHelper::exit);
instance.add(new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN),
ShutdownHelper::exit);
// Add the option to logout using "Control"+"Shift"+"L" if not in login scene
instance.addForNotExcluded(new KeyCodeCombination(KeyCode.L, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN),
UserUtil::logout,
SceneInfo.LOGIN_SCENE);
instance.addForNotExcluded(new KeyCodeCombination(KeyCode.L, KeyCombination.CONTROL_DOWN,
KeyCombination.SHIFT_DOWN),
UserUtil::logout,
SceneInfo.LOGIN_SCENE);
// Add option to open settings scene with "Control"+"S", if not in login scene
instance.addForNotExcluded(new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN),
() -> Context.getInstance().getSceneContext().load(SceneInfo.SETTINGS_SCENE),
SceneInfo.SETTINGS_SCENE,
SceneInfo.LOGIN_SCENE);
() -> Context.getInstance().getSceneContext()
.load(SceneInfo.SETTINGS_SCENE),
SceneInfo.SETTINGS_SCENE,
SceneInfo.LOGIN_SCENE);
}
}

View File

@ -14,7 +14,8 @@ import envoy.client.ui.SceneContext.SceneInfo;
*/
public final class GlobalKeyShortcuts {
private final EnumMap<SceneInfo, Map<KeyCombination, Runnable>> shortcuts = new EnumMap<>(SceneInfo.class);
private final EnumMap<SceneInfo, Map<KeyCombination, Runnable>> shortcuts =
new EnumMap<>(SceneInfo.class);
private static GlobalKeyShortcuts instance = new GlobalKeyShortcuts();
@ -36,16 +37,16 @@ public final class GlobalKeyShortcuts {
* @param action the action to perform
* @since Envoy Client v0.3-beta
*/
public void add(KeyCombination keys, Runnable action) { shortcuts.values().forEach(collection -> collection.put(keys, action)); }
public void add(KeyCombination keys, Runnable action) {
shortcuts.values().forEach(collection -> collection.put(keys, action));
}
/**
* Adds the given keyboard shortcut and its action to all scenes that are not
* part of exclude.
* Adds the given keyboard shortcut and its action to all scenes that are not part of exclude.
*
* @param keys the keys to press to perform the given action
* @param action the action to perform
* @param exclude the scenes that should be excluded from receiving this
* keyboard shortcut
* @param exclude the scenes that should be excluded from receiving this keyboard shortcut
* @since Envoy Client v0.3-beta
*/
public void addForNotExcluded(KeyCombination keys, Runnable action, SceneInfo... exclude) {
@ -53,10 +54,10 @@ public final class GlobalKeyShortcuts {
// Computing the remaining sceneInfos
final var include = new SceneInfo[SceneInfo.values().length - exclude.length];
int index = 0;
outer:
for (final var sceneInfo : SceneInfo.values()) {
outer: for (final var sceneInfo : SceneInfo.values()) {
for (final var excluded : exclude)
if (sceneInfo.equals(excluded)) continue outer;
if (sceneInfo.equals(excluded))
continue outer;
include[index++] = sceneInfo;
}
@ -72,5 +73,7 @@ public final class GlobalKeyShortcuts {
* @return all stored keyboard shortcuts for this scene
* @since Envoy Client v0.3-beta
*/
public Map<KeyCombination, Runnable> getKeyboardShortcuts(SceneInfo sceneInfo) { return shortcuts.get(sceneInfo); }
public Map<KeyCombination, Runnable> getKeyboardShortcuts(SceneInfo sceneInfo) {
return shortcuts.get(sceneInfo);
}
}

View File

@ -7,10 +7,9 @@ import javafx.scene.input.KeyCombination;
import envoy.client.ui.SceneContext;
/**
* Provides methods to set the keyboard shortcuts for a specific scene.
* Should only be implemented by controllers of scenes so that these methods can
* automatically be called inside {@link SceneContext} as soon
* as the underlying FXML file has been loaded.
* Provides methods to set the keyboard shortcuts for a specific scene. Should only be implemented
* by controllers of scenes so that these methods can automatically be called inside
* {@link SceneContext} as soon as the underlying FXML file has been loaded.
*
* @author Leon Hofmeister
* @since Envoy Client v0.3-beta

View File

@ -3,9 +3,8 @@ package envoy.client.event;
import envoy.event.Event.Valueless;
/**
* This event notifies various Envoy components of the application being about
* to shut down. This allows the graceful closing of connections, persisting
* local data etc.
* This event notifies various Envoy components of the application being about to shut down. This
* allows the graceful closing of connections, persisting local data etc.
*
* @author Leon Hofmeister
* @since Envoy Client v0.2-beta

View File

@ -16,5 +16,7 @@ public class MessageDeletion extends Event<Long> {
* @param messageID the ID of the deleted message
* @since Envoy Common v0.3-beta
*/
public MessageDeletion(long messageID) { super(messageID); }
public MessageDeletion(long messageID) {
super(messageID);
}
}

View File

@ -17,5 +17,7 @@ public class OwnStatusChange extends Event<UserStatus> {
* @param value the new user status of the client user
* @since Envoy Client v0.3-beta
*/
public OwnStatusChange(UserStatus value) { super(value); }
public OwnStatusChange(UserStatus value) {
super(value);
}
}

View File

@ -15,20 +15,19 @@ public final class AlertHelper {
private AlertHelper() {}
/**
* Asks for a confirmation dialog if {@link Settings#isAskForConfirmation()}
* returns {@code true}.
* Immediately executes the action if no dialog was requested or the dialog was
* exited with a confirmation.
* Does nothing if the dialog was closed without clicking on OK.
* Asks for a confirmation dialog if {@link Settings#isAskForConfirmation()} returns
* {@code true}. Immediately executes the action if no dialog was requested or the dialog was
* exited with a confirmation. Does nothing if the dialog was closed without clicking on OK.
*
* @param alert the (customized) alert to show. <strong>Should not be shown
* already</strong>
* @param alert the (customized) alert to show. <strong>Should not be shown already</strong>
* @param action the action to perform in case of success
* @since Envoy Client v0.2-beta
*/
public static void confirmAction(Alert alert, Runnable action) {
alert.setHeaderText("");
if (Settings.getInstance().isAskForConfirmation()) alert.showAndWait().filter(ButtonType.OK::equals).ifPresent(bu -> action.run());
else action.run();
if (Settings.getInstance().isAskForConfirmation())
alert.showAndWait().filter(ButtonType.OK::equals).ifPresent(bu -> action.run());
else
action.run();
}
}

View File

@ -1,11 +1,11 @@
package envoy.client.helper;
import dev.kske.eventbus.EventBus;
import envoy.client.data.*;
import envoy.client.event.EnvoyCloseEvent;
import envoy.client.ui.StatusTrayIcon;
import dev.kske.eventbus.EventBus;
/**
* Simplifies shutdown actions.
*
@ -22,18 +22,21 @@ public final class ShutdownHelper {
*
* @since Envoy Client v0.2-beta
*/
public static void exit() { exit(false); }
public static void exit() {
exit(false);
}
/**
* Exits Envoy immediately if {@code force = true},
* else it can exit or minimize Envoy, depending on the current state of
* {@link Settings#isHideOnClose()} and {@link StatusTrayIcon#isSupported()}.
* Exits Envoy immediately if {@code force = true}, else it can exit or minimize Envoy,
* depending on the current state of {@link Settings#isHideOnClose()} and
* {@link StatusTrayIcon#isSupported()}.
*
* @param force whether to close in any case.
* @since Envoy Client v0.2-beta
*/
public static void exit(boolean force) {
if (!force && Settings.getInstance().isHideOnClose() && StatusTrayIcon.isSupported()) Context.getInstance().getStage().setIconified(true);
if (!force && Settings.getInstance().isHideOnClose() && StatusTrayIcon.isSupported())
Context.getInstance().getStage().setIconified(true);
else {
EventBus.getInstance().dispatch(new EnvoyCloseEvent());
System.exit(0);

View File

@ -5,18 +5,19 @@ import java.net.Socket;
import java.util.concurrent.TimeoutException;
import java.util.logging.*;
import envoy.client.data.*;
import envoy.client.event.EnvoyCloseEvent;
import dev.kske.eventbus.*;
import dev.kske.eventbus.Event;
import envoy.data.*;
import envoy.event.*;
import envoy.util.*;
import dev.kske.eventbus.*;
import dev.kske.eventbus.Event;
import envoy.client.data.*;
import envoy.client.event.EnvoyCloseEvent;
/**
* Establishes a connection to the server, performs a handshake and delivers
* certain objects to the server.
* Establishes a connection to the server, performs a handshake and delivers certain objects to the
* server.
*
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
@ -44,26 +45,31 @@ public final class Client implements EventListener, Closeable {
*
* @since Envoy Client v0.2-beta
*/
public Client() { eventBus.registerListener(this); }
public Client() {
eventBus.registerListener(this);
}
/**
* Enters the online mode by acquiring a user ID from the server. As a
* connection has to be established and a handshake has to be made, this method
* will block for up to 5 seconds. If the handshake does exceed this time limit,
* an exception is thrown.
* Enters the online mode by acquiring a user ID from the server. As a connection has to be
* established and a handshake has to be made, this method will block for up to 5 seconds. If
* the handshake does exceed this time limit, an exception is thrown.
*
* @param credentials the login credentials of the user
* @param cacheMap the map of all caches needed
* @throws TimeoutException if the server could not be reached
* @throws IOException if the login credentials could not be written
* @throws InterruptedException if the current thread is interrupted while
* waiting for the handshake response
* @throws InterruptedException if the current thread is interrupted while waiting for the
* handshake response
*/
public void performHandshake(LoginCredentials credentials, CacheMap cacheMap) throws TimeoutException, IOException, InterruptedException {
if (online) throw new IllegalStateException("Handshake has already been performed successfully");
public void performHandshake(LoginCredentials credentials,
CacheMap cacheMap) throws TimeoutException, IOException,
InterruptedException {
if (online)
throw new IllegalStateException("Handshake has already been performed successfully");
// Establish TCP connection
logger.log(Level.FINER, String.format("Attempting connection to server %s:%d...", config.getServer(), config.getPort()));
logger.log(Level.FINER, String.format("Attempting connection to server %s:%d...",
config.getServer(), config.getPort()));
socket = new Socket(config.getServer(), config.getPort());
logger.log(Level.FINE, "Successfully established TCP connection to server");
@ -95,7 +101,8 @@ public final class Client implements EventListener, Closeable {
return;
}
if (System.currentTimeMillis() - start > 5000) throw new TimeoutException("Did not log in after 5 seconds");
if (System.currentTimeMillis() - start > 5000)
throw new TimeoutException("Did not log in after 5 seconds");
Thread.sleep(500);
}
@ -104,14 +111,12 @@ public final class Client implements EventListener, Closeable {
}
/**
* Initializes the {@link Receiver} used to process data sent from the server to
* this client.
* Initializes the {@link Receiver} used to process data sent from the server to this client.
*
* @param localDB the local database used to persist the current
* {@link IDGenerator}
* @param localDB the local database used to persist the current {@link IDGenerator}
* @param cacheMap the map of all caches needed
* @throws IOException if no {@link IDGenerator} is present and none could be
* requested from the server
* @throws IOException if no {@link IDGenerator} is present and none could be requested from the
* server
* @since Envoy Client v0.2-alpha
*/
public void initReceiver(LocalDB localDB, CacheMap cacheMap) throws IOException {
@ -127,7 +132,8 @@ public final class Client implements EventListener, Closeable {
cacheMap.get(GroupMessageStatusChange.class).setProcessor(eventBus::dispatch);
// Request a generator if none is present or the existing one is consumed
if (!localDB.hasIDGenerator() || !localDB.getIDGenerator().hasNext()) requestIDGenerator();
if (!localDB.hasIDGenerator() || !localDB.getIDGenerator().hasNext())
requestIDGenerator();
// Relay caches
cacheMap.getMap().values().forEach(Cache::relay);
@ -146,14 +152,14 @@ public final class Client implements EventListener, Closeable {
logger.log(Level.FINE, "Sending " + obj);
try {
SerializationUtils.writeBytesWithLength(obj, socket.getOutputStream());
} catch (IOException e) {
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
/**
* Sends a message to the server. The message's status will be incremented once
* it was delivered successfully.
* Sends a message to the server. The message's status will be incremented once it was delivered
* successfully.
*
* @param message the message to send
* @since Envoy Client v0.3-alpha
@ -174,7 +180,9 @@ public final class Client implements EventListener, Closeable {
}
@Event(eventType = HandshakeRejection.class, priority = 1000)
private void onHandshakeRejection() { rejected = true; }
private void onHandshakeRejection() {
rejected = true;
}
@Override
@Event(eventType = EnvoyCloseEvent.class, priority = 800)
@ -199,7 +207,10 @@ public final class Client implements EventListener, Closeable {
* @throws IllegalStateException if the client is not online
* @since Envoy Client v0.3-alpha
*/
private void checkOnline() throws IllegalStateException { if (!online) throw new IllegalStateException("Client is not online"); }
private void checkOnline() throws IllegalStateException {
if (!online)
throw new IllegalStateException("Client is not online");
}
/**
* @return the {@link User} as which this client is logged in

View File

@ -6,13 +6,12 @@ import java.util.*;
import java.util.function.Consumer;
import java.util.logging.*;
import envoy.util.*;
import dev.kske.eventbus.*;
import envoy.util.*;
/**
* Receives objects from the server and passes them to processor objects based
* on their class.
* Receives objects from the server and passes them to processor objects based on their class.
*
* @author Kai S. K. Engelbart
* @since Envoy Client v0.3-alpha
@ -40,8 +39,7 @@ public final class Receiver extends Thread {
}
/**
* Starts the receiver loop. When an object is read, it is passed to the
* appropriate processor.
* Starts the receiver loop. When an object is read, it is passed to the appropriate processor.
*
* @since Envoy Client v0.3-alpha
*/
@ -66,15 +64,19 @@ public final class Receiver extends Thread {
// Server has stopped sending, i.e. because he went offline
if (bytesRead == -1) {
isAlive = false;
logger.log(Level.INFO, "Lost connection to the server. Exiting receiver...");
logger.log(Level.INFO,
"Lost connection to the server. Exiting receiver...");
continue;
}
logger.log(Level.WARNING,
String.format("LV encoding violated: expected %d bytes, received %d bytes. Discarding object...", len, bytesRead));
String.format(
"LV encoding violated: expected %d bytes, received %d bytes. Discarding object...",
len, bytesRead));
continue;
}
try (ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(objBytes))) {
try (ObjectInputStream oin =
new ObjectInputStream(new ByteArrayInputStream(objBytes))) {
final Object obj = oin.readObject();
logger.log(Level.FINE, "Received " + obj);
@ -83,12 +85,17 @@ public final class Receiver extends Thread {
final Consumer processor = processors.get(obj.getClass());
// Dispatch to the processor if present
if (processor != null) processor.accept(obj);
if (processor != null)
processor.accept(obj);
// Dispatch to the event bus if the object is an event without a processor
else if (obj instanceof IEvent) eventBus.dispatch((IEvent) obj);
else if (obj instanceof IEvent)
eventBus.dispatch((IEvent) obj);
// Notify if no processor could be located
else logger.log(Level.WARNING,
String.format("The received object has the %s for which no processor is defined.", obj.getClass()));
else
logger.log(Level.WARNING,
String.format(
"The received object has the %s for which no processor is defined.",
obj.getClass()));
}
} catch (final SocketException | EOFException e) {
// Connection probably closed by client.
@ -100,14 +107,16 @@ public final class Receiver extends Thread {
}
/**
* Adds an object processor to this {@link Receiver}. It will be called once an
* object of the accepted class has been received.
* Adds an object processor to this {@link Receiver}. It will be called once an object of the
* accepted class has been received.
*
* @param processorClass the object class accepted by the processor
* @param processor the object processor
* @since Envoy Client v0.3-alpha
*/
public <T> void registerProcessor(Class<T> processorClass, Consumer<T> processor) { processors.put(processorClass, processor); }
public <T> void registerProcessor(Class<T> processorClass, Consumer<T> processor) {
processors.put(processorClass, processor);
}
/**
* Adds a map of object processors to this {@link Receiver}.
@ -115,12 +124,16 @@ public final class Receiver extends Thread {
* @param processors the processors to add the processors to add
* @since Envoy Client v0.1-beta
*/
public void registerProcessors(Map<Class<?>, ? extends Consumer<?>> processors) { this.processors.putAll(processors); }
public void registerProcessors(Map<Class<?>, ? extends Consumer<?>> processors) {
this.processors.putAll(processors);
}
/**
* Removes all object processors registered at this {@link Receiver}.
*
* @since Envoy Client v0.3-alpha
*/
public void removeAllProcessors() { processors.clear(); }
public void removeAllProcessors() {
processors.clear();
}
}

View File

@ -2,15 +2,15 @@ package envoy.client.net;
import java.util.logging.*;
import envoy.client.data.*;
import envoy.data.Message;
import envoy.event.MessageStatusChange;
import envoy.util.EnvoyLog;
import envoy.client.data.*;
/**
* Implements methods to send {@link Message}s and
* {@link MessageStatusChange}s to the server or cache them inside a
* {@link LocalDB} depending on the online status.
* Implements methods to send {@link Message}s and {@link MessageStatusChange}s to the server or
* cache them inside a {@link LocalDB} depending on the online status.
*
* @author Kai S. K. Engelbart
* @since Envoy Client v0.3-alpha
@ -23,12 +23,11 @@ public final class WriteProxy {
private static final Logger logger = EnvoyLog.getLogger(WriteProxy.class);
/**
* Initializes a write proxy using a client and a local database. The
* corresponding cache processors are injected into the caches.
* Initializes a write proxy using a client and a local database. The corresponding cache
* processors are injected into the caches.
*
* @param client the client instance used to send messages and events if online
* @param localDB the local database used to cache messages and events if
* offline
* @param localDB the local database used to cache messages and events if offline
* @since Envoy Client v0.3-alpha
*/
public WriteProxy(Client client, LocalDB localDB) {
@ -47,34 +46,39 @@ public final class WriteProxy {
}
/**
* Sends cached {@link Message}s and {@link MessageStatusChange}s to the
* server.
* Sends cached {@link Message}s and {@link MessageStatusChange}s to the server.
*
* @since Envoy Client v0.3-alpha
*/
public void flushCache() { localDB.getCacheMap().getMap().values().forEach(Cache::relay); }
public void flushCache() {
localDB.getCacheMap().getMap().values().forEach(Cache::relay);
}
/**
* Delivers a message to the server if online. Otherwise the message is cached
* inside the local database.
* Delivers a message to the server if online. Otherwise the message is cached inside the local
* database.
*
* @param message the message to send
* @since Envoy Client v0.3-alpha
*/
public void writeMessage(Message message) {
if (client.isOnline()) client.sendMessage(message);
else localDB.getCacheMap().getApplicable(Message.class).accept(message);
if (client.isOnline())
client.sendMessage(message);
else
localDB.getCacheMap().getApplicable(Message.class).accept(message);
}
/**
* Delivers a message status change event to the server if online. Otherwise the
* event is cached inside the local database.
* Delivers a message status change event to the server if online. Otherwise the event is cached
* inside the local database.
*
* @param evt the event to send
* @since Envoy Client v0.3-alpha
*/
public void writeMessageStatusChange(MessageStatusChange evt) {
if (client.isOnline()) client.send(evt);
else localDB.getCacheMap().getApplicable(MessageStatusChange.class).accept(evt);
if (client.isOnline())
client.send(evt);
else
localDB.getCacheMap().getApplicable(MessageStatusChange.class).accept(evt);
}
}

View File

@ -1,8 +1,8 @@
package envoy.client.ui;
/**
* This interface defines an action that should be performed when a scene gets
* restored from the scene stack in {@link SceneContext}.
* This interface defines an action that should be performed when a scene gets restored from the
* scene stack in {@link SceneContext}.
*
* @author Leon Hofmeister
* @since Envoy Client v0.1-beta
@ -12,8 +12,7 @@ public interface Restorable {
/**
* This method is getting called when a scene gets restored.<br>
* Hence, it can contain anything that should be done when the underlying scene
* gets restored.
* Hence, it can contain anything that should be done when the underlying scene gets restored.
*
* @since Envoy Client v0.1-beta
*/

View File

@ -9,20 +9,19 @@ import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.stage.Stage;
import dev.kske.eventbus.*;
import envoy.util.EnvoyLog;
import envoy.client.data.Settings;
import envoy.client.data.shortcuts.*;
import envoy.client.event.*;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.*;
/**
* Manages a stack of scenes. The most recently added scene is displayed inside
* a stage. When a scene is removed from the stack, its predecessor is
* displayed.
* Manages a stack of scenes. The most recently added scene is displayed inside a stage. When a
* scene is removed from the stack, its predecessor is displayed.
* <p>
* When a scene is loaded, the style sheet for the current theme is applied to
* it.
* When a scene is loaded, the style sheet for the current theme is applied to it.
*
* @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta
@ -63,7 +62,9 @@ public final class SceneContext implements EventListener {
*/
public final String path;
SceneInfo(String path) { this.path = path; }
SceneInfo(String path) {
this.path = path;
}
}
private final Stage stage;
@ -97,7 +98,8 @@ public final class SceneContext implements EventListener {
loader.setController(null);
try {
final var rootNode = (Parent) loader.load(getClass().getResourceAsStream(sceneInfo.path));
final var rootNode =
(Parent) loader.load(getClass().getResourceAsStream(sceneInfo.path));
final var scene = new Scene(rootNode);
final var controller = loader.getController();
controllerStack.push(controller);
@ -106,10 +108,13 @@ public final class SceneContext implements EventListener {
stage.setScene(scene);
// Supply the global custom keyboard shortcuts for that scene
scene.getAccelerators().putAll(GlobalKeyShortcuts.getInstance().getKeyboardShortcuts(sceneInfo));
scene.getAccelerators()
.putAll(GlobalKeyShortcuts.getInstance().getKeyboardShortcuts(sceneInfo));
// Supply the scene specific keyboard shortcuts
if (controller instanceof KeyboardMapping) scene.getAccelerators().putAll(((KeyboardMapping) controller).getKeyboardShortcuts());
if (controller instanceof KeyboardMapping)
scene.getAccelerators()
.putAll(((KeyboardMapping) controller).getKeyboardShortcuts());
// The LoginScene is the only scene not intended to be resized
// As strange as it seems, this is needed as otherwise the LoginScene won't be
@ -119,7 +124,9 @@ public final class SceneContext implements EventListener {
applyCSS();
stage.show();
} catch (final IOException e) {
EnvoyLog.getLogger(SceneContext.class).log(Level.SEVERE, String.format("Could not load scene for %s: ", sceneInfo), e);
EnvoyLog.getLogger(SceneContext.class)
.log(Level.SEVERE, String.format("Could not load scene for %s: ", sceneInfo),
e);
throw new RuntimeException(e);
}
}
@ -144,7 +151,8 @@ public final class SceneContext implements EventListener {
// If the controller implements the Restorable interface,
// the actions to perform on restoration will be executed here
final var controller = controllerStack.peek();
if (controller instanceof Restorable) ((Restorable) controller).onRestore();
if (controller instanceof Restorable)
((Restorable) controller).onRestore();
}
stage.show();
}
@ -154,7 +162,8 @@ public final class SceneContext implements EventListener {
final var styleSheets = stage.getScene().getStylesheets();
final var themeCSS = "/css/" + settings.getCurrentTheme() + ".css";
styleSheets.clear();
styleSheets.addAll(getClass().getResource("/css/base.css").toExternalForm(), getClass().getResource(themeCSS).toExternalForm());
styleSheets.addAll(getClass().getResource("/css/base.css").toExternalForm(),
getClass().getResource(themeCSS).toExternalForm());
}
}
@ -165,7 +174,9 @@ public final class SceneContext implements EventListener {
}
@Event(priority = 150, eventType = ThemeChangeEvent.class)
private void onThemeChange() { applyCSS(); }
private void onThemeChange() {
applyCSS();
}
/**
* @param <T> the type of the controller

View File

@ -10,6 +10,12 @@ import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.stage.Stage;
import envoy.data.*;
import envoy.data.User.UserStatus;
import envoy.event.*;
import envoy.exception.EnvoyException;
import envoy.util.EnvoyLog;
import envoy.client.data.*;
import envoy.client.data.shortcuts.EnvoyShortcutConfig;
import envoy.client.helper.ShutdownHelper;
@ -17,11 +23,6 @@ import envoy.client.net.Client;
import envoy.client.ui.SceneContext.SceneInfo;
import envoy.client.ui.controller.LoginScene;
import envoy.client.util.IconUtil;
import envoy.data.*;
import envoy.data.User.UserStatus;
import envoy.event.*;
import envoy.exception.EnvoyException;
import envoy.util.EnvoyLog;
/**
* Handles application startup.
@ -47,8 +48,8 @@ public final class Startup extends Application {
private static final Logger logger = EnvoyLog.getLogger(Startup.class);
/**
* Loads the configuration, initializes the client and the local database and
* delegates the rest of the startup process to {@link LoginScene}.
* Loads the configuration, initializes the client and the local database and delegates the rest
* of the startup process to {@link LoginScene}.
*
* @since Envoy Client v0.1-beta
*/
@ -57,7 +58,8 @@ public final class Startup extends Application {
// Initialize config and logger
try {
config.loadAll(Startup.class, "client.properties", getParameters().getRaw().toArray(new String[0]));
config.loadAll(Startup.class, "client.properties",
getParameters().getRaw().toArray(new String[0]));
EnvoyLog.initialize(config);
} catch (final IllegalStateException e) {
new Alert(AlertType.ERROR, "Error loading configuration values:\n" + e);
@ -97,7 +99,9 @@ public final class Startup extends Application {
logger.info("Attempting authentication with token...");
localDB.loadUserData();
if (!performHandshake(
LoginCredentials.loginWithToken(localDB.getUser().getName(), localDB.getAuthToken(), VERSION, localDB.getLastSync())))
LoginCredentials.loginWithToken(localDB.getUser().getName(),
localDB.getAuthToken(), VERSION,
localDB.getLastSync())))
sceneContext.load(SceneInfo.LOGIN_SCENE);
} else
// Load login scene
@ -117,7 +121,8 @@ public final class Startup extends Application {
cacheMap.put(GroupMessage.class, new Cache<GroupMessage>());
cacheMap.put(MessageStatusChange.class, new Cache<MessageStatusChange>());
cacheMap.put(GroupMessageStatusChange.class, new Cache<GroupMessageStatusChange>());
final var originalStatus = localDB.getUser() == null ? UserStatus.ONLINE : localDB.getUser().getStatus();
final var originalStatus =
localDB.getUser() == null ? UserStatus.ONLINE : localDB.getUser().getStatus();
try {
client.performHandshake(credentials, cacheMap);
if (client.isOnline()) {
@ -127,7 +132,8 @@ public final class Startup extends Application {
loadChatScene();
client.initReceiver(localDB, cacheMap);
return true;
} else return false;
} else
return false;
} catch (IOException | InterruptedException | TimeoutException e) {
logger.log(Level.INFO, "Could not connect to server. Entering offline mode...");
return attemptOfflineMode(credentials.getIdentifier());
@ -135,8 +141,8 @@ public final class Startup extends Application {
}
/**
* Attempts to load {@link envoy.client.ui.controller.ChatScene} in offline mode
* for a given user.
* Attempts to load {@link envoy.client.ui.controller.ChatScene} in offline mode for a given
* user.
*
* @param identifier the identifier of the user - currently his username
* @return whether the offline mode could be entered
@ -146,7 +152,8 @@ public final class Startup extends Application {
try {
// Try entering offline mode
final User clientUser = localDB.getUsers().get(identifier);
if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown");
if (clientUser == null)
throw new EnvoyException("Could not enter offline mode: user name unknown");
client.setSender(clientUser);
loadChatScene();
return true;
@ -187,7 +194,8 @@ public final class Startup extends Application {
} catch (final FileNotFoundException e) {
// The local database file has not yet been created, probably first login
} catch (final Exception e) {
new Alert(AlertType.ERROR, "Error while loading local database: " + e + "\nChats will not be stored locally.").showAndWait();
new Alert(AlertType.ERROR, "Error while loading local database: " + e
+ "\nChats will not be stored locally.").showAndWait();
logger.log(Level.WARNING, "Could not load local database: ", e);
}
@ -197,7 +205,8 @@ public final class Startup extends Application {
context.getWriteProxy().flushCache();
// Inform the server that this user has a different user status than expected
if (!user.getStatus().equals(UserStatus.ONLINE)) client.send(new UserStatusChange(user));
if (!user.getStatus().equals(UserStatus.ONLINE))
client.send(new UserStatusChange(user));
} else
// Set all contacts to offline mode
@ -211,7 +220,8 @@ public final class Startup extends Application {
final var stage = context.getStage();
// Pop LoginScene if present
if (!context.getSceneContext().isEmpty()) context.getSceneContext().pop();
if (!context.getSceneContext().isEmpty())
context.getSceneContext().pop();
// Load ChatScene
stage.setMinHeight(400);
@ -221,15 +231,22 @@ public final class Startup extends Application {
// Exit or minimize the stage when a close request occurs
stage.setOnCloseRequest(
e -> { ShutdownHelper.exit(); if (Settings.getInstance().isHideOnClose() && StatusTrayIcon.isSupported()) e.consume(); });
e -> {
ShutdownHelper.exit();
if (Settings.getInstance().isHideOnClose()
&& StatusTrayIcon.isSupported())
e.consume();
});
if (StatusTrayIcon.isSupported()) {
// Initialize status tray icon
final var trayIcon = new StatusTrayIcon(stage);
Settings.getInstance().getItems().get("hideOnClose").setChangeHandler(c -> {
if ((Boolean) c) trayIcon.show();
else trayIcon.hide();
if ((Boolean) c)
trayIcon.show();
else
trayIcon.hide();
});
}

View File

@ -6,14 +6,15 @@ import java.awt.TrayIcon.MessageType;
import javafx.application.Platform;
import javafx.stage.Stage;
import envoy.client.event.OwnStatusChange;
import envoy.client.helper.ShutdownHelper;
import envoy.client.util.*;
import dev.kske.eventbus.*;
import dev.kske.eventbus.Event;
import envoy.data.Message;
import envoy.data.User.UserStatus;
import dev.kske.eventbus.*;
import dev.kske.eventbus.Event;
import envoy.client.event.OwnStatusChange;
import envoy.client.helper.ShutdownHelper;
import envoy.client.util.*;
/**
* @author Kai S. K. Engelbart
@ -22,15 +23,14 @@ import dev.kske.eventbus.Event;
public final class StatusTrayIcon implements EventListener {
/**
* The {@link TrayIcon} provided by the System Tray API for controlling the
* system tray. This includes displaying the icon, but also creating
* notifications when new messages are received.
* The {@link TrayIcon} provided by the System Tray API for controlling the system tray. This
* includes displaying the icon, but also creating notifications when new messages are received.
*/
private final TrayIcon trayIcon;
/**
* A received {@link Message} is only displayed as a system tray notification if
* this variable is set to {@code true}.
* A received {@link Message} is only displayed as a system tray notification if this variable
* is set to {@code true}.
*/
private boolean displayMessages;
@ -41,11 +41,9 @@ public final class StatusTrayIcon implements EventListener {
public static boolean isSupported() { return SystemTray.isSupported(); }
/**
* Creates a {@link StatusTrayIcon} with the Envoy logo, a tool tip and a pop-up
* menu.
* Creates a {@link StatusTrayIcon} with the Envoy logo, a tool tip and a pop-up menu.
*
* @param stage the stage whose focus determines if message
* notifications are displayed
* @param stage the stage whose focus determines if message notifications are displayed
* @since Envoy Client v0.2-beta
*/
public StatusTrayIcon(Stage stage) {
@ -62,14 +60,18 @@ public final class StatusTrayIcon implements EventListener {
// Adding the logout menu item
final var logoutMenuItem = new MenuItem("Logout");
logoutMenuItem.addActionListener(evt -> { hide(); Platform.runLater(UserUtil::logout); });
logoutMenuItem.addActionListener(evt -> {
hide();
Platform.runLater(UserUtil::logout);
});
popup.add(logoutMenuItem);
// Adding the status change items
final var statusSubMenu = new Menu("Change status");
for (final var status : UserStatus.values()) {
final var statusMenuItem = new MenuItem(status.toString().toLowerCase());
statusMenuItem.addActionListener(evt -> Platform.runLater(() -> UserUtil.changeStatus(status)));
statusMenuItem
.addActionListener(evt -> Platform.runLater(() -> UserUtil.changeStatus(status)));
statusSubMenu.add(statusMenuItem);
}
popup.add(statusSubMenu);
@ -78,10 +80,15 @@ public final class StatusTrayIcon implements EventListener {
// Only display messages if the stage is not focused and the current user status
// is not BUSY (if BUSY, displayMessages will be false)
stage.focusedProperty().addListener((ov, wasFocused, isFocused) -> displayMessages = !displayMessages && wasFocused ? false : !isFocused);
stage.focusedProperty().addListener((ov, wasFocused, isFocused) -> displayMessages =
!displayMessages && wasFocused ? false : !isFocused);
// Show the window if the user clicks on the icon
trayIcon.addActionListener(evt -> Platform.runLater(() -> { stage.setIconified(false); stage.toFront(); stage.requestFocus(); }));
trayIcon.addActionListener(evt -> Platform.runLater(() -> {
stage.setIconified(false);
stage.toFront();
stage.requestFocus();
}));
// Start processing message events
EventBus.getInstance().registerListener(this);
@ -103,15 +110,22 @@ public final class StatusTrayIcon implements EventListener {
*
* @since Envoy Client v0.2-beta
*/
public void hide() { SystemTray.getSystemTray().remove(trayIcon); }
public void hide() {
SystemTray.getSystemTray().remove(trayIcon);
}
@Event
private void onOwnStatusChange(OwnStatusChange statusChange) { displayMessages = !statusChange.get().equals(UserStatus.BUSY); }
private void onOwnStatusChange(OwnStatusChange statusChange) {
displayMessages = !statusChange.get().equals(UserStatus.BUSY);
}
@Event
private void onMessage(Message message) {
if (displayMessages) trayIcon
.displayMessage(message.hasAttachment() ? "New " + message.getAttachment().getType().toString().toLowerCase() + " message received"
if (displayMessages)
trayIcon
.displayMessage(message.hasAttachment()
? "New " + message.getAttachment().getType().toString().toLowerCase()
+ " message received"
: "New message received", message.getText(), MessageType.INFO);
}
}

View File

@ -8,19 +8,19 @@ import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.skin.VirtualFlow;
import envoy.data.Message;
import envoy.data.User.UserStatus;
import envoy.util.EnvoyLog;
import envoy.client.data.Context;
import envoy.client.data.commands.*;
import envoy.client.helper.ShutdownHelper;
import envoy.client.ui.SceneContext.SceneInfo;
import envoy.client.ui.controller.ChatScene;
import envoy.client.util.*;
import envoy.data.Message;
import envoy.data.User.UserStatus;
import envoy.util.EnvoyLog;
/**
* Contains all {@link SystemCommand}s used for
* {@link envoy.client.ui.controller.ChatScene}.
* Contains all {@link SystemCommand}s used for {@link envoy.client.ui.controller.ChatScene}.
*
* @author Leon Hofmeister
* @since Envoy Client v0.3-beta
@ -29,12 +29,13 @@ public final class ChatSceneCommands {
private final ListView<Message> messageList;
private final SystemCommandMap messageTextAreaCommands = new SystemCommandMap();
private final SystemCommandBuilder builder = new SystemCommandBuilder(messageTextAreaCommands);
private final SystemCommandBuilder builder =
new SystemCommandBuilder(messageTextAreaCommands);
private static final String messageDependantCommandDescription = " the given message. Use s/S to use the selected message. Otherwise expects a number relative to the uppermost completely visible message.";
private static final String messageDependantCommandDescription =
" the given message. Use s/S to use the selected message. Otherwise expects a number relative to the uppermost completely visible message.";
/**
*
* @param messageList the message list to use for some commands
* @param chatScene the instance of {@code ChatScene} that uses this object
* @since Envoy Client v0.3-beta
@ -43,112 +44,153 @@ public final class ChatSceneCommands {
this.messageList = messageList;
// Error message initialization
builder.setAction(text -> { throw new RuntimeException(); }).setDescription("Shows an error message.").buildNoArg("error");
builder.setAction(text -> { throw new RuntimeException(); })
.setDescription("Shows an error message.").buildNoArg("error");
// Do A Barrel roll initialization
final var random = new Random();
builder.setAction(text -> chatScene.doABarrelRoll(Integer.parseInt(text.get(0)), Double.parseDouble(text.get(1))))
.setDefaults(Integer.toString(random.nextInt(3) + 1), Double.toString(random.nextDouble() * 3 + 1))
builder.setAction(text -> chatScene.doABarrelRoll(Integer.parseInt(text.get(0)),
Double.parseDouble(text.get(1))))
.setDefaults(Integer.toString(random.nextInt(3) + 1),
Double.toString(random.nextDouble() * 3 + 1))
.setDescription("See for yourself :)")
.setNumberOfArguments(2)
.build("dabr");
// Logout initialization
builder.setAction(text -> UserUtil.logout()).setDescription("Logs you out.").buildNoArg("logout");
builder.setAction(text -> UserUtil.logout()).setDescription("Logs you out.")
.buildNoArg("logout");
// Exit initialization
builder.setAction(text -> ShutdownHelper.exit()).setNumberOfArguments(0).setDescription("Exits the program.").build("exit", false);
builder.setAction(text -> ShutdownHelper.exit()).setNumberOfArguments(0)
.setDescription("Exits the program.").build("exit", false);
builder.build("q");
// Open settings scene initialization
builder.setAction(text -> Context.getInstance().getSceneContext().load(SceneInfo.SETTINGS_SCENE))
builder.setAction(text -> Context.getInstance().getSceneContext()
.load(SceneInfo.SETTINGS_SCENE))
.setDescription("Opens the settings screen")
.buildNoArg("settings");
// Status change initialization
builder.setAction(text -> {
try {
UserUtil.changeStatus(Enum.valueOf(UserStatus.class, text.get(0).toUpperCase()));
UserUtil.changeStatus(Enum.valueOf(UserStatus.class,
text.get(0).toUpperCase()));
} catch (final IllegalArgumentException e) {
final var alert = new Alert(AlertType.ERROR);
alert.setContentText("Please provide an existing status");
alert.showAndWait();
}
}).setDescription("Changes your status to the given status.").setNumberOfArguments(1).setDefaults("").build("status");
}).setDescription("Changes your status to the given status.")
.setNumberOfArguments(1)
.setDefaults("").build("status");
// Selection of a new message initialization
messageDependantAction("s",
m -> { messageList.getSelectionModel().clearSelection(); messageList.getSelectionModel().select(m); },
m -> true,
"Selects");
m -> {
messageList.getSelectionModel().clearSelection();
messageList.getSelectionModel().select(m);
},
m -> true,
"Selects");
// Copy text of selection initialization
messageDependantAction("cp", MessageUtil::copyMessageText, m -> !m.getText().isEmpty(), "Copies the text of");
messageDependantAction("cp", MessageUtil::copyMessageText, m -> !m.getText().isEmpty(),
"Copies the text of");
// Delete selection initialization
messageDependantAction("del", MessageUtil::deleteMessage, m -> true, "Deletes");
// Save attachment of selection initialization
messageDependantAction("save-att", MessageUtil::saveAttachment, Message::hasAttachment, "Saves the attachment of");
messageDependantAction("save-att", MessageUtil::saveAttachment, Message::hasAttachment,
"Saves the attachment of");
}
private void messageDependantAction(String command, Consumer<Message> action, Predicate<Message> additionalCheck, String description) {
private void messageDependantAction(String command, Consumer<Message> action,
Predicate<Message> additionalCheck, String description) {
builder.setAction(text -> {
final var positionalArgument = text.get(0).toLowerCase();
// the currently selected message was requested
if (positionalArgument.startsWith("s")) {
final var relativeString = positionalArgument.length() == 1 ? "" : positionalArgument.substring(1);
final var relativeString =
positionalArgument.length() == 1 ? "" : positionalArgument.substring(1);
// Only s has been used as input
if (positionalArgument.length() == 1) {
final var selectedMessage = messageList.getSelectionModel().getSelectedItem();
if (selectedMessage != null && additionalCheck.test(selectedMessage)) action.accept(selectedMessage);
final var selectedMessage =
messageList.getSelectionModel().getSelectedItem();
if (selectedMessage != null && additionalCheck.test(selectedMessage))
action.accept(selectedMessage);
return;
// Either s++ or s-- has been requested
} else if (relativeString.equals("++") || relativeString.equals("--")) selectionNeighbor(action, additionalCheck, positionalArgument);
} else if (relativeString.equals("++") || relativeString.equals("--"))
selectionNeighbor(action, additionalCheck, positionalArgument);
// A message relative to the currently selected message should be used (i.e.
// s+4)
else useRelativeMessage(command, action, additionalCheck, relativeString, true);
else
useRelativeMessage(command, action, additionalCheck, relativeString,
true);
// Either ++s or --s has been requested
} else if (positionalArgument.equals("--s") || positionalArgument.equals("++s"))
selectionNeighbor(action, additionalCheck, positionalArgument);
// Just a number is expected: ((+)4)
else useRelativeMessage(command, action, additionalCheck, positionalArgument, false);
}).setDefaults("s").setNumberOfArguments(1).setDescription(description.concat(messageDependantCommandDescription)).build(command);
else
useRelativeMessage(command, action, additionalCheck, positionalArgument,
false);
}).setDefaults("s").setNumberOfArguments(1)
.setDescription(description.concat(messageDependantCommandDescription))
.build(command);
}
private void selectionNeighbor(Consumer<Message> action, Predicate<Message> additionalCheck, final String positionalArgument) {
final var wantedIndex = messageList.getSelectionModel().getSelectedIndex() + (positionalArgument.contains("+") ? 1 : -1);
private void selectionNeighbor(Consumer<Message> action, Predicate<Message> additionalCheck,
final String positionalArgument) {
final var wantedIndex = messageList.getSelectionModel().getSelectedIndex()
+ (positionalArgument.contains("+") ? 1 : -1);
messageList.getSelectionModel().clearAndSelect(wantedIndex);
final var selectedMessage = messageList.getItems().get(wantedIndex);
if (selectedMessage != null && additionalCheck.test(selectedMessage)) action.accept(selectedMessage);
if (selectedMessage != null && additionalCheck.test(selectedMessage))
action.accept(selectedMessage);
}
private void useRelativeMessage(String command, Consumer<Message> action, Predicate<Message> additionalCheck, final String positionalArgument,
boolean useSelectedMessage) throws NumberFormatException {
final var stripPlus = positionalArgument.startsWith("+") ? positionalArgument.substring(1) : positionalArgument;
private void useRelativeMessage(String command, Consumer<Message> action,
Predicate<Message> additionalCheck,
final String positionalArgument,
boolean useSelectedMessage) throws NumberFormatException {
final var stripPlus =
positionalArgument.startsWith("+") ? positionalArgument.substring(1)
: positionalArgument;
final var incDec = Integer.valueOf(stripPlus);
try {
// The currently selected message is the base message
if (useSelectedMessage) {
final var messageToUse = messageList.getItems().get(messageList.getSelectionModel().getSelectedIndex() + incDec);
if (messageToUse != null && additionalCheck.test(messageToUse)) action.accept(messageToUse);
final var messageToUse =
messageList.getItems()
.get(messageList.getSelectionModel().getSelectedIndex() + incDec);
if (messageToUse != null && additionalCheck.test(messageToUse))
action.accept(messageToUse);
// The currently upmost completely visible message is the base message
} else {
final var messageToUse = messageList.getItems()
.get(((VirtualFlow<?>) messageList.lookup(".virtual-flow")).getFirstVisibleCell().getIndex() + 1 + incDec);
if (messageToUse != null && additionalCheck.test(messageToUse)) action.accept(messageToUse);
.get(
((VirtualFlow<?>) messageList.lookup(".virtual-flow")).getFirstVisibleCell()
.getIndex()
+ 1 + incDec);
if (messageToUse != null && additionalCheck.test(messageToUse))
action.accept(messageToUse);
}
} catch (final IndexOutOfBoundsException e) {
EnvoyLog.getLogger(ChatSceneCommands.class)
.log(Level.INFO, " A non-existing message was requested by the user for System command " + command);
.log(Level.INFO,
" A non-existing message was requested by the user for System command "
+ command);
}
}

View File

@ -7,8 +7,8 @@ import javafx.scene.control.*;
import javafx.scene.input.Clipboard;
/**
* Displays a context menu that offers an additional option when one of
* its menu items has been clicked.
* Displays a context menu that offers an additional option when one of its menu items has been
* clicked.
* <p>
* Current options are:
* <ul>
@ -24,9 +24,8 @@ import javafx.scene.input.Clipboard;
*
* @author Leon Hofmeister
* @since Envoy Client v0.2-beta
* @apiNote please refrain from using
* {@link ContextMenu#setOnShowing(EventHandler)} as this is already
* used by this component
* @apiNote please refrain from using {@link ContextMenu#setOnShowing(EventHandler)} as this is
* already used by this component
*/
public class TextInputContextMenu extends ContextMenu {
@ -40,8 +39,8 @@ public class TextInputContextMenu extends ContextMenu {
private final MenuItem selectAllMI = new MenuItem("Select all");
/**
* Creates a new {@code TextInputContextMenu} with an optional action when
* this menu was clicked. Currently shows:
* Creates a new {@code TextInputContextMenu} with an optional action when this menu was
* clicked. Currently shows:
* <ul>
* <li>undo</li>
* <li>redo</li>
@ -53,14 +52,12 @@ public class TextInputContextMenu extends ContextMenu {
* <li>Select all</li>
* </ul>
*
* @param control the text input component to display this
* {@code ContextMenu}
* @param menuItemClicked the second action to perform when a menu item of this
* context menu has been clicked
* @param control the text input component to display this {@code ContextMenu}
* @param menuItemClicked the second action to perform when a menu item of this context menu has
* been clicked
* @since Envoy Client v0.2-beta
* @apiNote please refrain from using
* {@link ContextMenu#setOnShowing(EventHandler)} as this is already
* used by this component
* @apiNote please refrain from using {@link ContextMenu#setOnShowing(EventHandler)} as this is
* already used by this component
*/
public TextInputContextMenu(TextInputControl control, Consumer<ActionEvent> menuItemClicked) {
@ -100,7 +97,11 @@ public class TextInputContextMenu extends ContextMenu {
getItems().add(selectAllMI);
}
private EventHandler<ActionEvent> addAction(Consumer<ActionEvent> originalAction, Consumer<ActionEvent> additionalAction) {
return e -> { originalAction.accept(e); additionalAction.accept(e); };
private EventHandler<ActionEvent> addAction(Consumer<ActionEvent> originalAction,
Consumer<ActionEvent> additionalAction) {
return e -> {
originalAction.accept(e);
additionalAction.accept(e);
};
}
}

View File

@ -6,10 +6,11 @@ import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.HBox;
import envoy.client.data.audio.AudioPlayer;
import envoy.exception.EnvoyException;
import envoy.util.EnvoyLog;
import envoy.client.data.audio.AudioPlayer;
/**
* Enables the play back of audio clips through a button.
*
@ -18,7 +19,7 @@ import envoy.util.EnvoyLog;
*/
public final class AudioControl extends HBox {
private AudioPlayer player = new AudioPlayer();
private final AudioPlayer player = new AudioPlayer();
private static final Logger logger = EnvoyLog.getLogger(AudioControl.class);
@ -29,11 +30,11 @@ public final class AudioControl extends HBox {
* @since Envoy Client v0.1-beta
*/
public AudioControl(byte[] audioData) {
var button = new Button("Play");
final var button = new Button("Play");
button.setOnAction(e -> {
try {
player.play(audioData);
} catch (EnvoyException ex) {
} catch (final EnvoyException ex) {
logger.log(Level.SEVERE, "Could not play back audio: ", ex);
new Alert(AlertType.ERROR, "Could not play back audio").showAndWait();
}

View File

@ -9,8 +9,8 @@ import envoy.client.data.*;
import envoy.client.util.IconUtil;
/**
* Displays a chat using a contact control for the recipient and a label for the
* unread message count.
* Displays a chat using a contact control for the recipient and a label for the unread message
* count.
*
* @see ContactControl
* @author Leon Hofmeister
@ -19,7 +19,7 @@ import envoy.client.util.IconUtil;
public final class ChatControl extends HBox {
private static final Image userIcon = IconUtil.loadIconThemeSensitive("user_icon", 32),
groupIcon = IconUtil.loadIconThemeSensitive("group_icon", 32);
groupIcon = IconUtil.loadIconThemeSensitive("group_icon", 32);
/**
* Creates a new {@code ChatControl}.
@ -32,7 +32,8 @@ public final class ChatControl extends HBox {
setPadding(new Insets(0, 0, 3, 0));
// Profile picture
final var contactProfilePic = new ProfilePicImageView(chat instanceof GroupChat ? groupIcon : userIcon, 32);
final var contactProfilePic =
new ProfilePicImageView(chat instanceof GroupChat ? groupIcon : userIcon, 32);
getChildren().add(contactProfilePic);
// Spacing

View File

@ -6,9 +6,8 @@ import javafx.scene.layout.VBox;
import envoy.data.*;
/**
* Displays information about a contact in two rows. The first row contains the
* name. The second row contains the online status (user) or the member count
* (group).
* Displays information about a contact in two rows. The first row contains the name. The second row
* contains the online status (user) or the member count (group).
*
* @author Kai S. K. Engelbart
* @since Envoy Client v0.2-beta
@ -29,23 +28,24 @@ public final class ContactControl extends VBox {
getChildren().add(nameLabel);
// Online status (user) or member count (group)
getChildren().add(contact instanceof User ? new UserStatusLabel((User) contact) : new GroupSizeLabel((Group) contact));
getChildren().add(contact instanceof User ? new UserStatusLabel((User) contact)
: new GroupSizeLabel((Group) contact));
getStyleClass().add("list-element");
}
/**
* Replaces the info label of this {@code ContactControl} with an updated
* version.
* Replaces the info label of this {@code ContactControl} with an updated version.
* <p>
* This method should be called when the status of the underlying user or the
* size of the underlying group has changed.
* This method should be called when the status of the underlying user or the size of the
* underlying group has changed.
*
* @since Envoy Client v0.3-beta
* @apiNote will produce buggy results if contact control gets updated so that
* the info label is no longer on index 1.
* @apiNote will produce buggy results if contact control gets updated so that the info label is
* no longer on index 1.
*/
public void replaceInfoLabel() {
getChildren().set(1, contact instanceof User ? new UserStatusLabel((User) contact) : new GroupSizeLabel((Group) contact));
getChildren().set(1, contact instanceof User ? new UserStatusLabel((User) contact)
: new GroupSizeLabel((Group) contact));
}
}

View File

@ -16,5 +16,7 @@ public final class GroupSizeLabel extends Label {
* @param recipient the group whose members to show
* @since Envoy Client v0.3-beta
*/
public GroupSizeLabel(Group recipient) { super(recipient.getContacts().size() + " members"); }
public GroupSizeLabel(Group recipient) {
super(recipient.getContacts().size() + " members");
}
}

View File

@ -11,13 +11,14 @@ import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import envoy.client.data.*;
import envoy.client.net.Client;
import envoy.client.util.*;
import envoy.data.*;
import envoy.data.Message.MessageStatus;
import envoy.util.EnvoyLog;
import envoy.client.data.*;
import envoy.client.net.Client;
import envoy.client.util.*;
/**
* This class transforms a single {@link Message} into a UI component.
*
@ -33,13 +34,15 @@ public final class MessageControl extends Label {
private final Client client = context.getClient();
private static final Context context = Context.getInstance();
private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")
.withZone(ZoneId.systemDefault());
private static final Map<MessageStatus, Image> statusImages = IconUtil.loadByEnum(MessageStatus.class, 16);
private static final Logger logger = EnvoyLog.getLogger(MessageControl.class);
private static final DateTimeFormatter dateFormat =
DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")
.withZone(ZoneId.systemDefault());
private static final Map<MessageStatus, Image> statusImages =
IconUtil.loadByEnum(MessageStatus.class, 16);
private static final Logger logger =
EnvoyLog.getLogger(MessageControl.class);
/**
*
* @param message the message that should be formatted
* @since Envoy Client v0.1-beta
*/
@ -107,7 +110,10 @@ public final class MessageControl extends Label {
switch (message.getAttachment().getType()) {
case PICTURE:
vbox.getChildren()
.add(new ImageView(new Image(new ByteArrayInputStream(message.getAttachment().getData()), 256, 256, true, true)));
.add(new ImageView(new Image(
new ByteArrayInputStream(message.getAttachment()
.getData()),
256, 256, true, true)));
break;
case VIDEO:
break;
@ -138,7 +144,8 @@ public final class MessageControl extends Label {
hBoxBottom.setAlignment(Pos.BOTTOM_RIGHT);
getStyleClass().add("own-message");
hbox.setAlignment(Pos.CENTER_RIGHT);
} else getStyleClass().add("received-message");
} else
getStyleClass().add("received-message");
vbox.getChildren().add(hBoxBottom);
// Adjusting height and weight of the cell to the corresponding ListView
paddingProperty().setValue(new Insets(5, 20, 5, 20));
@ -146,11 +153,13 @@ public final class MessageControl extends Label {
setGraphic(vbox);
}
private void loadMessageInfoScene(Message message) { logger.log(Level.FINEST, "message info scene was requested for " + message); }
private void loadMessageInfoScene(Message message) {
logger.log(Level.FINEST, "message info scene was requested for " + message);
}
/**
* @return whether the message stored by this {@code MessageControl} has been
* sent by this user of Envoy
* @return whether the message stored by this {@code MessageControl} has been sent by this user
* of Envoy
* @since Envoy Client v0.1-beta
*/
public boolean isOwnMessage() { return ownMessage; }

View File

@ -16,7 +16,9 @@ public final class ProfilePicImageView extends ImageView {
*
* @since Envoy Client v0.2-beta
*/
public ProfilePicImageView() { this(null); }
public ProfilePicImageView() {
this(null);
}
/**
* Creates a new {@code ProfilePicImageView}.
@ -24,17 +26,20 @@ public final class ProfilePicImageView extends ImageView {
* @param image the image to display
* @since Envoy Client v0.2-beta
*/
public ProfilePicImageView(Image image) { this(image, 40); }
public ProfilePicImageView(Image image) {
this(image, 40);
}
/**
* Creates a new {@code ProfilePicImageView}.
*
* @param image the image to display
* @param sizeAndRounding the size and rounding for a circular
* {@code ProfilePicImageView}
* @param sizeAndRounding the size and rounding for a circular {@code ProfilePicImageView}
* @since Envoy Client v0.2-beta
*/
public ProfilePicImageView(Image image, double sizeAndRounding) { this(image, sizeAndRounding, sizeAndRounding); }
public ProfilePicImageView(Image image, double sizeAndRounding) {
this(image, sizeAndRounding, sizeAndRounding);
}
/**
* Creates a new {@code ProfilePicImageView}.

View File

@ -8,25 +8,26 @@ import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.scene.shape.Rectangle;
import envoy.data.User;
import envoy.client.util.IconUtil;
import envoy.data.*;
/**
* Displays an {@link User} as a quick select control which is used in the
* quick select list.
* Displays an {@link User} as a quick select control which is used in the quick select list.
*
* @author Maximilian K&auml;fer
* @since Envoy Client v0.3-beta
*/
public class QuickSelectControl extends VBox {
private User user;
private final User user;
/**
* Creates an instance of the {@code QuickSelectControl}.
*
* @param user the contact whose data is used to create this instance.
* @param action the action to perform when a contact is removed with this control as a parameter
* @param user the contact whose data is used to create this instance.
* @param action the action to perform when a contact is removed with this control as a
* parameter
* @since Envoy Client v0.3-beta
*/
public QuickSelectControl(User user, Consumer<QuickSelectControl> action) {
@ -35,16 +36,17 @@ public class QuickSelectControl extends VBox {
setPrefWidth(37);
setMaxWidth(37);
setMinWidth(37);
var stackPane = new StackPane();
final var stackPane = new StackPane();
stackPane.setAlignment(Pos.TOP_CENTER);
// Profile picture
var picHold = new VBox();
final var picHold = new VBox();
picHold.setPadding(new Insets(2, 0, 0, 0));
picHold.setPrefHeight(35);
picHold.setMaxHeight(35);
picHold.setMinHeight(35);
var contactProfilePic = new ImageView(IconUtil.loadIconThemeSensitive("user_icon", 32));
final var contactProfilePic =
new ImageView(IconUtil.loadIconThemeSensitive("user_icon", 32));
final var clip = new Rectangle();
clip.setWidth(32);
clip.setHeight(32);
@ -54,15 +56,15 @@ public class QuickSelectControl extends VBox {
picHold.getChildren().add(contactProfilePic);
stackPane.getChildren().add(picHold);
var hBox = new HBox();
final var hBox = new HBox();
hBox.setPrefHeight(12);
hBox.setMaxHeight(12);
hBox.setMinHeight(12);
var region = new Region();
final var region = new Region();
hBox.getChildren().add(region);
HBox.setHgrow(region, Priority.ALWAYS);
var removeBtn = new Button();
final var removeBtn = new Button();
removeBtn.setPrefSize(12, 12);
removeBtn.setMaxSize(12, 12);
removeBtn.setMinSize(12, 12);
@ -72,7 +74,7 @@ public class QuickSelectControl extends VBox {
stackPane.getChildren().add(hBox);
getChildren().add(stackPane);
var nameLabel = new Label();
final var nameLabel = new Label();
nameLabel.setPrefSize(35, 20);
nameLabel.setMaxSize(35, 20);
nameLabel.setMinSize(35, 20);

View File

@ -24,6 +24,17 @@ import javafx.scene.shape.Rectangle;
import javafx.stage.FileChooser;
import javafx.util.Duration;
import dev.kske.eventbus.*;
import dev.kske.eventbus.Event;
import envoy.data.*;
import envoy.data.Attachment.AttachmentType;
import envoy.data.Message.MessageStatus;
import envoy.event.*;
import envoy.event.contact.ContactOperation;
import envoy.exception.EnvoyException;
import envoy.util.EnvoyLog;
import envoy.client.data.*;
import envoy.client.data.audio.AudioRecorder;
import envoy.client.event.*;
@ -33,16 +44,6 @@ import envoy.client.ui.chatscene.*;
import envoy.client.ui.control.*;
import envoy.client.ui.listcell.*;
import envoy.client.util.*;
import envoy.data.*;
import envoy.data.Attachment.AttachmentType;
import envoy.data.Message.MessageStatus;
import envoy.event.*;
import envoy.event.contact.ContactOperation;
import envoy.exception.EnvoyException;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.*;
import dev.kske.eventbus.Event;
/**
* Controller for the chat scene.
@ -142,9 +143,11 @@ public final class ChatScene implements EventListener, Restorable {
private final WriteProxy writeProxy = context.getWriteProxy();
private final SceneContext sceneContext = context.getSceneContext();
private final AudioRecorder recorder = new AudioRecorder();
private final Tooltip onlyIfOnlineTooltip = new Tooltip("You need to be online to do this");
private final Tooltip onlyIfOnlineTooltip =
new Tooltip("You need to be online to do this");
private static Image DEFAULT_ATTACHMENT_VIEW_IMAGE = IconUtil.loadIconThemeSensitive("attachment_present", 20);
private static Image DEFAULT_ATTACHMENT_VIEW_IMAGE =
IconUtil.loadIconThemeSensitive("attachment_present", 20);
private static final Settings settings = Settings.getInstance();
private static final EventBus eventBus = EventBus.getInstance();
@ -170,14 +173,19 @@ public final class ChatScene implements EventListener, Restorable {
// JavaFX provides an internal way of populating the context menu of a text
// area.
// We, however, need additional functionality.
messageTextArea.setContextMenu(new TextInputContextMenu(messageTextArea, e -> checkKeyCombination(null)));
messageTextArea.setContextMenu(new TextInputContextMenu(messageTextArea,
e -> checkKeyCombination(null)));
// Set the icons of buttons and image views
settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", DEFAULT_ICON_SIZE)));
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
attachmentButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("attachment", DEFAULT_ICON_SIZE)));
settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings",
DEFAULT_ICON_SIZE)));
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone",
DEFAULT_ICON_SIZE)));
attachmentButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("attachment",
DEFAULT_ICON_SIZE)));
attachmentView.setImage(DEFAULT_ATTACHMENT_VIEW_IMAGE);
messageSearchButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("search", DEFAULT_ICON_SIZE)));
messageSearchButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("search",
DEFAULT_ICON_SIZE)));
clientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
onlyIfOnlineTooltip.setShowDelay(Duration.millis(250));
final var clip = new Rectangle();
@ -199,15 +207,19 @@ public final class ChatScene implements EventListener, Restorable {
// no check will be performed in case it has already been disabled - a negative
// GroupCreationResult might have been returned
if (!newGroupButton.isDisabled()) newGroupButton.setDisable(!online);
if (!newGroupButton.isDisabled())
newGroupButton.setDisable(!online);
newContactButton.setDisable(!online);
if (online) try {
Tooltip.uninstall(contactSpecificOnlineOperations, onlyIfOnlineTooltip);
contactSearchTab.setContent(new FXMLLoader().load(getClass().getResourceAsStream("/fxml/ContactSearchTab.fxml")));
groupCreationTab.setContent(new FXMLLoader().load(getClass().getResourceAsStream("/fxml/GroupCreationTab.fxml")));
} catch (final IOException e) {
logger.log(Level.SEVERE, "An error occurred when attempting to load tabs: ", e);
}
if (online)
try {
Tooltip.uninstall(contactSpecificOnlineOperations, onlyIfOnlineTooltip);
contactSearchTab.setContent(new FXMLLoader()
.load(getClass().getResourceAsStream("/fxml/ContactSearchTab.fxml")));
groupCreationTab.setContent(new FXMLLoader()
.load(getClass().getResourceAsStream("/fxml/GroupCreationTab.fxml")));
} catch (final IOException e) {
logger.log(Level.SEVERE, "An error occurred when attempting to load tabs: ", e);
}
else {
Tooltip.install(contactSpecificOnlineOperations, onlyIfOnlineTooltip);
updateInfoLabel("You are offline", "info-label-warning");
@ -216,7 +228,9 @@ public final class ChatScene implements EventListener, Restorable {
}
@Event(eventType = BackEvent.class)
private void onBackEvent() { tabPane.getSelectionModel().select(Tabs.CONTACT_LIST.ordinal()); }
private void onBackEvent() {
tabPane.getSelectionModel().select(Tabs.CONTACT_LIST.ordinal());
}
@Event(includeSubtypes = true)
private void onMessage(Message message) {
@ -225,7 +239,9 @@ public final class ChatScene implements EventListener, Restorable {
// Exceptions: this user is the sender (sync) or group message (group is
// recipient)
final var ownMessage = message.getSenderID() == localDB.getUser().getID();
final var recipientID = message instanceof GroupMessage || ownMessage ? message.getRecipientID() : message.getSenderID();
final var recipientID =
message instanceof GroupMessage || ownMessage ? message.getRecipientID()
: message.getSenderID();
localDB.getChat(recipientID).ifPresent(chat -> {
Platform.runLater(() -> {
@ -235,13 +251,15 @@ public final class ChatScene implements EventListener, Restorable {
if (chat.equals(currentChat)) {
currentChat.read(writeProxy);
scrollToMessageListEnd();
} else if (!ownMessage && message.getStatus() != MessageStatus.READ) chat.incrementUnreadAmount();
} else if (!ownMessage && message.getStatus() != MessageStatus.READ)
chat.incrementUnreadAmount();
// Move chat with most recent unread messages to the top
chats.getSource().remove(chat);
((ObservableList<Chat>) chats.getSource()).add(0, chat);
if (chat.equals(currentChat)) chatList.getSelectionModel().select(0);
if (chat.equals(currentChat))
chatList.getSelectionModel().select(0);
});
});
}
@ -251,9 +269,10 @@ public final class ChatScene implements EventListener, Restorable {
// Update UI if in current chat and the current user was the sender of the
// message
if (currentChat != null) localDB.getMessage(evt.getID())
.filter(msg -> msg.getSenderID() == client.getSender().getID())
.ifPresent(msg -> Platform.runLater(messageList::refresh));
if (currentChat != null)
localDB.getMessage(evt.getID())
.filter(msg -> msg.getSenderID() == client.getSender().getID())
.ifPresent(msg -> Platform.runLater(messageList::refresh));
}
@Event
@ -275,12 +294,15 @@ public final class ChatScene implements EventListener, Restorable {
final var contact = operation.get();
switch (operation.getOperationType()) {
case ADD:
if (contact instanceof User) localDB.getUsers().put(contact.getName(), (User) contact);
final var chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact);
if (contact instanceof User)
localDB.getUsers().put(contact.getName(), (User) contact);
final var chat = contact instanceof User ? new Chat(contact)
: new GroupChat(client.getSender(), contact);
Platform.runLater(() -> ((ObservableList<Chat>) chats.getSource()).add(0, chat));
break;
case REMOVE:
Platform.runLater(() -> chats.getSource().removeIf(c -> c.getRecipient().equals(contact)));
Platform.runLater(() -> chats.getSource()
.removeIf(c -> c.getRecipient().equals(contact)));
break;
}
}
@ -299,30 +321,43 @@ public final class ChatScene implements EventListener, Restorable {
}
@Event
private void onGroupCreationResult(GroupCreationResult result) { Platform.runLater(() -> newGroupButton.setDisable(!result.get())); }
private void onGroupCreationResult(GroupCreationResult result) {
Platform.runLater(() -> newGroupButton.setDisable(!result.get()));
}
@Event(eventType = ThemeChangeEvent.class)
private void onThemeChange() {
settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", DEFAULT_ICON_SIZE)));
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
attachmentButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("attachment", DEFAULT_ICON_SIZE)));
settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings",
DEFAULT_ICON_SIZE)));
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone",
DEFAULT_ICON_SIZE)));
attachmentButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("attachment",
DEFAULT_ICON_SIZE)));
DEFAULT_ATTACHMENT_VIEW_IMAGE = IconUtil.loadIconThemeSensitive("attachment_present", 20);
attachmentView.setImage(isCustomAttachmentImage ? attachmentView.getImage() : DEFAULT_ATTACHMENT_VIEW_IMAGE);
messageSearchButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("search", DEFAULT_ICON_SIZE)));
attachmentView.setImage(isCustomAttachmentImage ? attachmentView.getImage()
: DEFAULT_ATTACHMENT_VIEW_IMAGE);
messageSearchButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("search",
DEFAULT_ICON_SIZE)));
clientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
chatList.setCellFactory(new ListCellFactory<>(ChatControl::new));
messageList.setCellFactory(MessageListCell::new);
// TODO: cache image
if (currentChat != null)
if (currentChat.getRecipient() instanceof User) recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
else recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("group_icon", 43));
if (currentChat.getRecipient() instanceof User)
recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
else
recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("group_icon", 43));
}
@Event(eventType = Logout.class, priority = 200)
private void onLogout() { eventBus.removeListener(this); }
private void onLogout() {
eventBus.removeListener(this);
}
@Override
public void onRestore() { updateRemainingCharsLabel(); }
public void onRestore() {
updateRemainingCharsLabel();
}
/**
* Actions to perform when the list of contacts has been clicked.
@ -331,7 +366,8 @@ public final class ChatScene implements EventListener, Restorable {
*/
@FXML
private void chatListClicked() {
if (chatList.getSelectionModel().isEmpty()) return;
if (chatList.getSelectionModel().isEmpty())
return;
final var user = chatList.getSelectionModel().getSelectedItem().getRecipient();
if (user != null && (currentChat == null || !user.equals(currentChat.getRecipient()))) {
@ -353,7 +389,8 @@ public final class ChatScene implements EventListener, Restorable {
// Discard the pending attachment
if (recorder.isRecording()) {
recorder.cancel();
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone",
DEFAULT_ICON_SIZE)));
voiceButton.setText(null);
}
pendingAttachment = null;
@ -361,7 +398,10 @@ public final class ChatScene implements EventListener, Restorable {
remainingChars.setVisible(true);
remainingChars
.setText(String.format("remaining chars: %d/%d", MAX_MESSAGE_LENGTH - messageTextArea.getText().length(), MAX_MESSAGE_LENGTH));
.setText(String.format("remaining chars: %d/%d",
MAX_MESSAGE_LENGTH
- messageTextArea.getText().length(),
MAX_MESSAGE_LENGTH));
}
messageTextArea.setDisable(currentChat == null || postingPermanentlyDisabled);
voiceButton.setDisable(!recorder.isSupported());
@ -376,7 +416,8 @@ public final class ChatScene implements EventListener, Restorable {
topBarStatusLabel.getStyleClass().add(status.toLowerCase());
recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
} else {
topBarStatusLabel.setText(currentChat.getRecipient().getContacts().size() + " members");
topBarStatusLabel.setText(currentChat.getRecipient().getContacts().size()
+ " members");
topBarStatusLabel.getStyleClass().clear();
recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("group_icon", 43));
}
@ -397,7 +438,9 @@ public final class ChatScene implements EventListener, Restorable {
* @since Envoy Client v0.1-beta
*/
@FXML
private void settingsButtonClicked() { sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE); }
private void settingsButtonClicked() {
sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE);
}
/**
* Actions to perform when the "Add Contact" - Button has been clicked.
@ -405,10 +448,14 @@ public final class ChatScene implements EventListener, Restorable {
* @since Envoy Client v0.1-beta
*/
@FXML
private void addContactButtonClicked() { tabPane.getSelectionModel().select(Tabs.CONTACT_SEARCH.ordinal()); }
private void addContactButtonClicked() {
tabPane.getSelectionModel().select(Tabs.CONTACT_SEARCH.ordinal());
}
@FXML
private void groupCreationButtonClicked() { tabPane.getSelectionModel().select(Tabs.GROUP_CREATION.ordinal()); }
private void groupCreationButtonClicked() {
tabPane.getSelectionModel().select(Tabs.GROUP_CREATION.ordinal());
}
@FXML
private void voiceButtonClicked() {
@ -417,15 +464,21 @@ public final class ChatScene implements EventListener, Restorable {
if (!recorder.isRecording()) {
Platform.runLater(() -> {
voiceButton.setText("Recording");
voiceButton.setGraphic(new ImageView(IconUtil.loadIcon("microphone_recording", DEFAULT_ICON_SIZE)));
voiceButton
.setGraphic(new ImageView(IconUtil.loadIcon("microphone_recording",
DEFAULT_ICON_SIZE)));
});
recorder.start();
} else {
pendingAttachment = new Attachment(recorder.finish(), "Voice_recording_"
+ DateTimeFormatter.ofPattern("yyyy_MM_dd-HH_mm_ss").format(LocalDateTime.now()) + "." + AudioRecorder.FILE_FORMAT,
AttachmentType.VOICE);
+ DateTimeFormatter.ofPattern("yyyy_MM_dd-HH_mm_ss")
.format(LocalDateTime.now())
+ "." + AudioRecorder.FILE_FORMAT,
AttachmentType.VOICE);
Platform.runLater(() -> {
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
voiceButton
.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone",
DEFAULT_ICON_SIZE)));
voiceButton.setText(null);
checkPostConditions(false);
updateAttachmentView(true);
@ -433,7 +486,8 @@ public final class ChatScene implements EventListener, Restorable {
}
} catch (final EnvoyException e) {
logger.log(Level.SEVERE, "Could not record audio: ", e);
Platform.runLater(new Alert(AlertType.ERROR, "Could not record audio")::showAndWait);
Platform.runLater(new Alert(AlertType.ERROR,
"Could not record audio")::showAndWait);
}
}).start();
}
@ -446,16 +500,18 @@ public final class ChatScene implements EventListener, Restorable {
fileChooser.setTitle("Add Attachment");
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
fileChooser.getExtensionFilters()
.addAll(new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg", "*.bmp", "*.gif"),
new FileChooser.ExtensionFilter("Videos", "*.mp4"),
new FileChooser.ExtensionFilter("All Files", "*.*"));
.addAll(new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg", "*.bmp",
"*.gif"),
new FileChooser.ExtensionFilter("Videos", "*.mp4"),
new FileChooser.ExtensionFilter("All Files", "*.*"));
final var file = fileChooser.showOpenDialog(sceneContext.getStage());
if (file != null) {
// Check max file size
if (file.length() > 16E6) {
new Alert(AlertType.WARNING, "The selected file exceeds the size limit of 16MB!").showAndWait();
new Alert(AlertType.WARNING,
"The selected file exceeds the size limit of 16MB!").showAndWait();
return;
}
@ -477,7 +533,9 @@ public final class ChatScene implements EventListener, Restorable {
checkPostConditions(false);
// Setting the preview image as image of the attachmentView
if (type == AttachmentType.PICTURE) {
attachmentView.setImage(new Image(new ByteArrayInputStream(fileBytes), DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE, true, true));
attachmentView.setImage(new Image(new ByteArrayInputStream(fileBytes),
DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE, true,
true));
isCustomAttachmentImage = true;
}
attachmentView.setVisible(true);
@ -488,8 +546,7 @@ public final class ChatScene implements EventListener, Restorable {
}
/**
* Rotates every element in our application by {@code rotations}*360° in
* {@code an}.
* Rotates every element in our application by {@code rotations}*360° in {@code an}.
*
* @param rotations the amount of times the scene is rotated by 360°
* @param animationTime the time in seconds that this animation lasts
@ -506,7 +563,8 @@ public final class ChatScene implements EventListener, Restorable {
final var rotatableNodes = ReflectionUtil.getAllDeclaredNodeVariables(this);
for (final var node : rotatableNodes) {
// Sets the animation duration to {animationTime}
final var rotateTransition = new RotateTransition(Duration.seconds(animationTime), node);
final var rotateTransition =
new RotateTransition(Duration.seconds(animationTime), node);
// rotates every element {rotations} times
rotateTransition.setByAngle(rotations * 360);
rotateTransition.play();
@ -517,9 +575,8 @@ public final class ChatScene implements EventListener, Restorable {
}
/**
* Checks the text length of the {@code messageTextArea}, adjusts the
* {@code remainingChars} label and checks whether to send the message
* automatically.
* Checks the text length of the {@code messageTextArea}, adjusts the {@code remainingChars}
* label and checks whether to send the message automatically.
*
* @param e the key event that will be analyzed for a post request
* @since Envoy Client v0.1-beta
@ -532,29 +589,33 @@ public final class ChatScene implements EventListener, Restorable {
// Sending an IsTyping event if none has been sent for
// IsTyping#millisecondsActive
if (client.isOnline() && currentChat.getLastWritingEvent() + IsTyping.millisecondsActive <= System.currentTimeMillis()) {
if (client.isOnline() && currentChat.getLastWritingEvent()
+ IsTyping.millisecondsActive <= System.currentTimeMillis()) {
client.send(new IsTyping(getChatID(), currentChat.getRecipient().getID()));
currentChat.lastWritingEventWasNow();
}
// KeyPressed will be called before the char has been added to the text, hence
// this is needed for the first char
if (messageTextArea.getText().length() == 1 && e != null) checkPostConditions(e);
if (messageTextArea.getText().length() == 1 && e != null)
checkPostConditions(e);
// This is needed for the messageTA context menu
else if (e == null) checkPostConditions(false);
else if (e == null)
checkPostConditions(false);
}
/**
* Returns the id that should be used to send things to the server: the id of
* 'our' {@link User} if the recipient of that object is another User, else the
* id of the {@link Group} 'our' user is sending to.
* Returns the id that should be used to send things to the server: the id of 'our' {@link User}
* if the recipient of that object is another User, else the id of the {@link Group} 'our' user
* is sending to.
*
* @return an id that can be sent to the server
* @since Envoy Client v0.2-beta
*/
private long getChatID() {
return currentChat.getRecipient() instanceof User ? client.getSender().getID() : currentChat.getRecipient().getID();
return currentChat.getRecipient() instanceof User ? client.getSender().getID()
: currentChat.getRecipient().getID();
}
/**
@ -564,21 +625,27 @@ public final class ChatScene implements EventListener, Restorable {
@FXML
private void checkPostConditions(KeyEvent e) {
final var enterPressed = e.getCode() == KeyCode.ENTER;
final var messagePosted = enterPressed ? settings.isEnterToSend() ? !e.isControlDown() : e.isControlDown() : false;
final var messagePosted =
enterPressed ? settings.isEnterToSend() ? !e.isControlDown() : e.isControlDown()
: false;
if (messagePosted) {
// Removing an inserted line break if added by pressing enter
final var text = messageTextArea.getText();
final var textPosition = messageTextArea.getCaretPosition() - 1;
if (!e.isControlDown() && !text.isEmpty() && text.charAt(textPosition) == '\n')
messageTextArea.setText(new StringBuilder(text).deleteCharAt(textPosition).toString());
messageTextArea.setText(new StringBuilder(text).deleteCharAt(textPosition)
.toString());
}
// if control is pressed, the enter press is originally invalidated. Here it'll
// be inserted again
else if (enterPressed && e.isControlDown()) {
var caretPosition = messageTextArea.getCaretPosition();
messageTextArea.setText(new StringBuilder(messageTextArea.getText()).insert(caretPosition, '\n').toString());
messageTextArea.setText(new StringBuilder(messageTextArea.getText())
.insert(caretPosition,
'\n')
.toString());
messageTextArea.positionCaret(++caretPosition);
}
checkPostConditions(messagePosted);
@ -586,8 +653,10 @@ public final class ChatScene implements EventListener, Restorable {
private void checkPostConditions(boolean postMessage) {
if (!postingPermanentlyDisabled) {
if (!postButton.isDisabled() && postMessage) postMessage();
postButton.setDisable(messageTextArea.getText().isBlank() && pendingAttachment == null || currentChat == null);
if (!postButton.isDisabled() && postMessage)
postMessage();
postButton.setDisable(messageTextArea.getText().isBlank() && pendingAttachment == null
|| currentChat == null);
} else {
final var noMoreMessaging = "Go online to send messages";
if (!infoLabel.getText().equals(noMoreMessaging))
@ -621,13 +690,14 @@ public final class ChatScene implements EventListener, Restorable {
private void updateRemainingCharsLabel() {
final var currentLength = messageTextArea.getText().length();
final var remainingLength = MAX_MESSAGE_LENGTH - currentLength;
remainingChars.setText(String.format("remaining chars: %d/%d", remainingLength, MAX_MESSAGE_LENGTH));
remainingChars.setText(String.format("remaining chars: %d/%d", remainingLength,
MAX_MESSAGE_LENGTH));
remainingChars.setTextFill(Color.rgb(currentLength, remainingLength, 0, 1));
}
/**
* Sends a new {@link Message} or {@link GroupMessage} to the server based on
* the text entered in the {@code messageTextArea} and the given attachment.
* Sends a new {@link Message} or {@link GroupMessage} to the server based on the text entered
* in the {@code messageTextArea} and the given attachment.
*
* @since Envoy Client v0.1-beta
*/
@ -644,8 +714,10 @@ public final class ChatScene implements EventListener, Restorable {
final var text = messageTextArea.getText().strip();
if (!commands.getChatSceneCommands().executeIfPresent(text)) {
// Creating the message and its metadata
final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
.setText(text);
final var builder =
new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(),
localDB.getIDGenerator())
.setText(text);
// Setting an attachment, if present
if (pendingAttachment != null) {
builder.setAttachment(pendingAttachment);
@ -653,8 +725,9 @@ public final class ChatScene implements EventListener, Restorable {
updateAttachmentView(false);
}
// Building the final message
final var message = currentChat.getRecipient() instanceof Group ? builder.buildGroupMessage((Group) currentChat.getRecipient())
: builder.build();
final var message = currentChat.getRecipient() instanceof Group
? builder.buildGroupMessage((Group) currentChat.getRecipient())
: builder.build();
// Send message
writeProxy.writeMessage(message);
@ -672,7 +745,8 @@ public final class ChatScene implements EventListener, Restorable {
scrollToMessageListEnd();
// Request a new ID generator if all IDs were used
if (!localDB.getIDGenerator().hasNext() && client.isOnline()) client.requestIDGenerator();
if (!localDB.getIDGenerator().hasNext() && client.isOnline())
client.requestIDGenerator();
}
// Clear text field and disable post button
@ -687,14 +761,16 @@ public final class ChatScene implements EventListener, Restorable {
*
* @since Envoy Client v0.1-beta
*/
private void scrollToMessageListEnd() { messageList.scrollTo(messageList.getItems().size() - 1); }
private void scrollToMessageListEnd() {
messageList.scrollTo(messageList.getItems().size() - 1);
}
/**
* Updates the {@code infoLabel}.
*
* @param text the text to use
* @param infoLabelID the id the the {@code infoLabel} should have so that it
* can be styled accordingly in CSS
* @param infoLabelID the id the the {@code infoLabel} should have so that it can be styled
* accordingly in CSS
* @since Envoy Client v0.1-beta
*/
private void updateInfoLabel(String text, String infoLabelID) {
@ -705,14 +781,15 @@ public final class ChatScene implements EventListener, Restorable {
/**
* Updates the {@code attachmentView} in terms of visibility.<br>
* Additionally resets the shown image to {@code DEFAULT_ATTACHMENT_VIEW_IMAGE}
* if another image is currently present.
* Additionally resets the shown image to {@code DEFAULT_ATTACHMENT_VIEW_IMAGE} if another image
* is currently present.
*
* @param visible whether the {@code attachmentView} should be displayed
* @since Envoy Client v0.1-beta
*/
private void updateAttachmentView(boolean visible) {
if (!attachmentView.getImage().equals(DEFAULT_ATTACHMENT_VIEW_IMAGE)) attachmentView.setImage(DEFAULT_ATTACHMENT_VIEW_IMAGE);
if (!attachmentView.getImage().equals(DEFAULT_ATTACHMENT_VIEW_IMAGE))
attachmentView.setImage(DEFAULT_ATTACHMENT_VIEW_IMAGE);
attachmentView.setVisible(visible);
}
@ -734,12 +811,15 @@ public final class ChatScene implements EventListener, Restorable {
// Context menu actions
@FXML
private void deleteContact() { try {} catch (final NullPointerException e) {} }
private void deleteContact() {
try {} catch (final NullPointerException e) {}
}
@FXML
private void copyAndPostMessage() {
final var messageText = messageTextArea.getText();
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(messageText), null);
Toolkit.getDefaultToolkit().getSystemClipboard()
.setContents(new StringSelection(messageText), null);
final var image = attachmentView.getImage();
final var messageAttachment = pendingAttachment;
postMessage();
@ -747,7 +827,8 @@ public final class ChatScene implements EventListener, Restorable {
updateRemainingCharsLabel();
postButton.setDisable(messageText.isBlank());
attachmentView.setImage(image);
if (attachmentView.getImage() != null) attachmentView.setVisible(true);
if (attachmentView.getImage() != null)
attachmentView.setVisible(true);
pendingAttachment = messageAttachment;
}
@ -756,11 +837,14 @@ public final class ChatScene implements EventListener, Restorable {
*
* @since Envoy Client v0.3-beta
*/
public void clearMessageSelection() { messageList.getSelectionModel().clearSelection(); }
public void clearMessageSelection() {
messageList.getSelectionModel().clearSelection();
}
@FXML
private void searchContacts() {
chats.setPredicate(contactSearch.getText().isBlank() ? c -> true
: c -> c.getRecipient().getName().toLowerCase().contains(contactSearch.getText().toLowerCase()));
: c -> c.getRecipient().getName().toLowerCase()
.contains(contactSearch.getText().toLowerCase()));
}
}

View File

@ -7,28 +7,28 @@ import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
import dev.kske.eventbus.*;
import envoy.data.User;
import envoy.event.ElementOperation;
import envoy.event.contact.*;
import envoy.util.EnvoyLog;
import envoy.client.data.Context;
import envoy.client.event.BackEvent;
import envoy.client.helper.AlertHelper;
import envoy.client.net.Client;
import envoy.client.ui.control.ContactControl;
import envoy.client.ui.listcell.ListCellFactory;
import envoy.data.User;
import envoy.event.ElementOperation;
import envoy.event.contact.*;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.*;
/**
* Provides a search bar in which a user name (substring) can be entered. The
* users with a matching name are then displayed inside a list view. A
* {@link UserSearchRequest} is sent on every keystroke.
* Provides a search bar in which a user name (substring) can be entered. The users with a matching
* name are then displayed inside a list view. A {@link UserSearchRequest} is sent on every
* keystroke.
* <p>
* <i>The actual search algorithm is implemented on the server.
* <p>
* To create a group, a button is available that loads the
* {@link GroupCreationTab}.
* To create a group, a button is available that loads the {@link GroupCreationTab}.
*
* @author Leon Hofmeister
* @author Maximilian K&auml;fer
@ -59,16 +59,22 @@ public class ContactSearchTab implements EventListener {
@Event
private void onUserSearchResult(UserSearchResult result) {
Platform.runLater(() -> { userList.getItems().clear(); userList.getItems().addAll(result.get()); });
Platform.runLater(() -> {
userList.getItems().clear();
userList.getItems().addAll(result.get());
});
}
@Event
private void onContactOperation(ContactOperation operation) {
final var contact = operation.get();
if (operation.getOperationType() == ElementOperation.ADD) Platform.runLater(() -> {
userList.getItems().remove(contact);
if (currentlySelectedUser != null && currentlySelectedUser.equals(contact) && alert.isShowing()) alert.close();
});
if (operation.getOperationType() == ElementOperation.ADD)
Platform.runLater(() -> {
userList.getItems().remove(contact);
if (currentlySelectedUser != null && currentlySelectedUser.equals(contact)
&& alert.isShowing())
alert.close();
});
}
/**
@ -79,13 +85,15 @@ public class ContactSearchTab implements EventListener {
@FXML
private void sendRequest() {
final var text = searchBar.getText().strip();
if (!text.isBlank()) client.send(new UserSearchRequest(text));
else userList.getItems().clear();
if (!text.isBlank())
client.send(new UserSearchRequest(text));
else
userList.getItems().clear();
}
/**
* Clears the text in the search bar and the items shown in the list.
* Additionally disables both clear and search button.
* Clears the text in the search bar and the items shown in the list. Additionally disables both
* clear and search button.
*
* @since Envoy Client v0.1-beta
*/
@ -96,8 +104,7 @@ public class ContactSearchTab implements EventListener {
}
/**
* Sends an {@link ContactOperation} for the selected user to the
* server.
* Sends an {@link ContactOperation} for the selected user to the server.
*
* @since Envoy Client v0.1-beta
*/
@ -106,7 +113,8 @@ public class ContactSearchTab implements EventListener {
final var user = userList.getSelectionModel().getSelectedItem();
if (user != null) {
currentlySelectedUser = user;
alert.setContentText("Add user " + currentlySelectedUser.getName() + " to your contacts?");
alert.setContentText("Add user " + currentlySelectedUser.getName()
+ " to your contacts?");
AlertHelper.confirmAction(alert, this::addAsContact);
}
}
@ -124,5 +132,7 @@ public class ContactSearchTab implements EventListener {
}
@FXML
private void backButtonClicked() { eventBus.dispatch(new BackEvent()); }
private void backButtonClicked() {
eventBus.dispatch(new BackEvent());
}
}

View File

@ -10,25 +10,25 @@ import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import envoy.client.data.*;
import envoy.client.event.BackEvent;
import envoy.client.ui.control.*;
import envoy.client.ui.listcell.ListCellFactory;
import dev.kske.eventbus.*;
import envoy.data.*;
import envoy.event.GroupCreation;
import envoy.event.contact.ContactOperation;
import envoy.util.Bounds;
import dev.kske.eventbus.*;
import envoy.client.data.*;
import envoy.client.event.BackEvent;
import envoy.client.ui.control.*;
import envoy.client.ui.listcell.ListCellFactory;
/**
* Provides a group creation interface. A group name can be entered in the text
* field at the top. Available users (local chat recipients) are displayed
* inside a list and can be selected (multiple selection available).
* Provides a group creation interface. A group name can be entered in the text field at the top.
* Available users (local chat recipients) are displayed inside a list and can be selected (multiple
* selection available).
* <p>
* When the group creation button is pressed, a {@link GroupCreation} is sent to
* the server. This controller enforces a valid group name and a non-empty
* member list (excluding the client user).
* When the group creation button is pressed, a {@link GroupCreation} is sent to the server. This
* controller enforces a valid group name and a non-empty member list (excluding the client user).
*
* @author Maximilian K&auml;fer
* @since Envoy Client v0.1-beta
@ -82,7 +82,7 @@ public class GroupCreationTab implements EventListener {
.map(User.class::cast)
.collect(Collectors.toList()));
resizeQuickSelectSpace(0);
quickSelectList.addEventFilter(MouseEvent.MOUSE_PRESSED, evt -> evt.consume());
quickSelectList.addEventFilter(MouseEvent.MOUSE_PRESSED, MouseEvent::consume);
}
/**
@ -93,8 +93,12 @@ public class GroupCreationTab implements EventListener {
@FXML
private void userListClicked() {
if (userList.getSelectionModel().getSelectedItem() != null) {
quickSelectList.getItems().add(new QuickSelectControl(userList.getSelectionModel().getSelectedItem(), this::removeFromQuickSelection));
createButton.setDisable(quickSelectList.getItems().isEmpty() || groupNameField.getText().isBlank());
quickSelectList.getItems()
.add(new QuickSelectControl(userList.getSelectionModel()
.getSelectedItem(),
this::removeFromQuickSelection));
createButton.setDisable(quickSelectList.getItems().isEmpty()
|| groupNameField.getText().isBlank());
resizeQuickSelectSpace(60);
userList.getItems().remove(userList.getSelectionModel().getSelectedItem());
userList.getSelectionModel().clearSelection();
@ -102,13 +106,16 @@ public class GroupCreationTab implements EventListener {
}
/**
* Checks, whether the {@code createButton} can be enabled because text is
* present in the text field.
* Checks, whether the {@code createButton} can be enabled because text is present in the text
* field.
*
* @since Envoy Client v0.1-beta
*/
@FXML
private void textUpdated() { createButton.setDisable(quickSelectList.getItems().isEmpty() || groupNameField.getText().isBlank()); }
private void textUpdated() {
createButton.setDisable(quickSelectList.getItems().isEmpty()
|| groupNameField.getText().isBlank());
}
/**
* Sends a {@link GroupCreation} to the server and closes this scene.
@ -152,19 +159,22 @@ public class GroupCreationTab implements EventListener {
private void createGroup(String name) {
Context.getInstance()
.getClient()
.send(new GroupCreation(name, quickSelectList.getItems().stream().map(q -> q.getUser().getID()).collect(Collectors.toSet())));
.send(new GroupCreation(name,
quickSelectList.getItems().stream()
.map(q -> q.getUser().getID())
.collect(Collectors.toSet())));
}
/**
* Returns true if the proposed group name is already present in the users
* {@code LocalDB}.
* Returns true if the proposed group name is already present in the users {@code LocalDB}.
*
* @param newName the chosen group name
* @return true if this name is already present
* @since Envoy Client v0.1-beta
*/
public boolean groupNameAlreadyPresent(String newName) {
return localDB.getChats().stream().map(Chat::getRecipient).filter(Group.class::isInstance).map(Contact::getName).anyMatch(newName::equals);
return localDB.getChats().stream().map(Chat::getRecipient).filter(Group.class::isInstance)
.map(Contact::getName).anyMatch(newName::equals);
}
/**
@ -235,15 +245,16 @@ public class GroupCreationTab implements EventListener {
@Event
private void onContactOperation(ContactOperation operation) {
if (operation.get() instanceof User) Platform.runLater(() -> {
switch (operation.getOperationType()) {
case ADD:
userList.getItems().add((User) operation.get());
break;
case REMOVE:
userList.getItems().removeIf(operation.get()::equals);
break;
}
});
if (operation.get() instanceof User)
Platform.runLater(() -> {
switch (operation.getOperationType()) {
case ADD:
userList.getItems().add((User) operation.get());
break;
case REMOVE:
userList.getItems().removeIf(operation.get()::equals);
break;
}
});
}
}

View File

@ -10,14 +10,15 @@ import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.image.ImageView;
import envoy.client.data.ClientConfig;
import envoy.client.ui.*;
import envoy.client.util.IconUtil;
import dev.kske.eventbus.*;
import envoy.data.LoginCredentials;
import envoy.event.HandshakeRejection;
import envoy.util.*;
import dev.kske.eventbus.*;
import envoy.client.data.ClientConfig;
import envoy.client.ui.Startup;
import envoy.client.util.IconUtil;
/**
* Controller for the login scene.
@ -78,25 +79,31 @@ public final class LoginScene implements EventListener {
@FXML
private void loginButtonPressed() {
final String user = userTextField.getText(), pass = passwordField.getText(), repeatPass = repeatPasswordField.getText();
final String user = userTextField.getText(), pass = passwordField.getText(),
repeatPass = repeatPasswordField.getText();
final boolean requestToken = cbStaySignedIn.isSelected();
// Prevent registration with unequal passwords
if (registration && !pass.equals(repeatPass)) {
new Alert(AlertType.ERROR, "The entered password is unequal to the repeated one").showAndWait();
new Alert(AlertType.ERROR,
"The entered password is unequal to the repeated one").showAndWait();
repeatPasswordField.clear();
} else if (!Bounds.isValidContactName(user)) {
new Alert(AlertType.ERROR, "The entered user name is not valid (" + Bounds.CONTACT_NAME_PATTERN + ")").showAndWait();
new Alert(AlertType.ERROR, "The entered user name is not valid ("
+ Bounds.CONTACT_NAME_PATTERN + ")").showAndWait();
userTextField.clear();
} else {
Instant lastSync = Startup.loadLastSync(userTextField.getText());
Startup.performHandshake(registration ? LoginCredentials.registration(user, pass, requestToken, Startup.VERSION, lastSync)
: LoginCredentials.login(user, pass, requestToken, Startup.VERSION, lastSync));
final Instant lastSync = Startup.loadLastSync(userTextField.getText());
Startup.performHandshake(registration
? LoginCredentials.registration(user, pass, requestToken, Startup.VERSION, lastSync)
: LoginCredentials.login(user, pass, requestToken, Startup.VERSION, lastSync));
}
}
@FXML
private void offlineModeButtonPressed() { Startup.attemptOfflineMode(userTextField.getText()); }
private void offlineModeButtonPressed() {
Startup.attemptOfflineMode(userTextField.getText());
}
@FXML
private void registerSwitchPressed() {
@ -127,5 +134,7 @@ public final class LoginScene implements EventListener {
}
@Event
private void onHandshakeRejection(HandshakeRejection evt) { Platform.runLater(() -> new Alert(AlertType.ERROR, evt.get()).showAndWait()); }
private void onHandshakeRejection(HandshakeRejection evt) {
Platform.runLater(() -> new Alert(AlertType.ERROR, evt.get()).showAndWait());
}
}

View File

@ -28,7 +28,8 @@ public final class SettingsScene implements KeyboardMapping {
@FXML
private void initialize() {
settingsList.setCellFactory(new ListCellFactory<>(pane -> new Label(pane.getTitle())));
settingsList.getItems().addAll(new GeneralSettingsPane(), new UserSettingsPane(), new DownloadSettingsPane(), new BugReportPane());
settingsList.getItems().addAll(new GeneralSettingsPane(), new UserSettingsPane(),
new DownloadSettingsPane(), new BugReportPane());
}
@FXML
@ -41,10 +42,13 @@ public final class SettingsScene implements KeyboardMapping {
}
@FXML
private void backButtonClicked() { Context.getInstance().getSceneContext().pop(); }
private void backButtonClicked() {
Context.getInstance().getSceneContext().pop();
}
@Override
public Map<KeyCombination, Runnable> getKeyboardShortcuts() {
return Map.of(new KeyCodeCombination(KeyCode.B, KeyCombination.CONTROL_DOWN), this::backButtonClicked);
return Map.of(new KeyCodeCombination(KeyCode.B, KeyCombination.CONTROL_DOWN),
this::backButtonClicked);
}
}

View File

@ -31,9 +31,8 @@ public abstract class AbstractListCell<T, U extends Node> extends ListCell<T> {
if (!(empty || item == null)) {
setCursor(Cursor.HAND);
setGraphic(renderItem(item));
} else {
} else
setGraphic(null);
}
}
/**

View File

@ -28,5 +28,7 @@ public final class GenericListCell<T, U extends Node> extends AbstractListCell<T
}
@Override
protected U renderItem(T item) { return renderer.apply(item); }
protected U renderItem(T item) {
return renderer.apply(item);
}
}

View File

@ -7,15 +7,15 @@ import javafx.scene.control.*;
import javafx.util.Callback;
/**
* Provides a creation mechanism for generic list cells given a list view and a
* conversion function.
* Provides a creation mechanism for generic list cells given a list view and a conversion function.
*
* @author Kai S. K. Engelbart
* @param <T> the type of object to display
* @param <U> the type of node displayed
* @since Envoy Client v0.1-beta
*/
public final class ListCellFactory<T, U extends Node> implements Callback<ListView<T>, ListCell<T>> {
public final class ListCellFactory<T, U extends Node>
implements Callback<ListView<T>, ListCell<T>> {
private final Function<? super T, U> renderer;
@ -23,8 +23,12 @@ public final class ListCellFactory<T, U extends Node> implements Callback<ListVi
* @param renderer a function converting the type to display into a node
* @since Envoy Client v0.1-beta
*/
public ListCellFactory(Function<? super T, U> renderer) { this.renderer = renderer; }
public ListCellFactory(Function<? super T, U> renderer) {
this.renderer = renderer;
}
@Override
public ListCell<T> call(ListView<T> listView) { return new GenericListCell<>(listView, renderer); }
public ListCell<T> call(ListView<T> listView) {
return new GenericListCell<>(listView, renderer);
}
}

View File

@ -3,9 +3,10 @@ package envoy.client.ui.listcell;
import javafx.geometry.*;
import javafx.scene.control.ListView;
import envoy.client.ui.control.MessageControl;
import envoy.data.Message;
import envoy.client.ui.control.MessageControl;
/**
* A list cell containing messages represented as message controls.
*
@ -18,20 +19,27 @@ public final class MessageListCell extends AbstractListCell<Message, MessageCont
* @param listView the list view inside of which the cell will be displayed
* @since Envoy Client v0.1-beta
*/
public MessageListCell(ListView<? extends Message> listView) { super(listView); }
public MessageListCell(ListView<? extends Message> listView) {
super(listView);
}
@Override
protected MessageControl renderItem(Message message) {
final var control = new MessageControl(message);
listView.widthProperty().addListener((observable, oldValue, newValue) -> adjustPadding(newValue.intValue(), control.isOwnMessage()));
listView.widthProperty()
.addListener((observable, oldValue,
newValue) -> adjustPadding(newValue.intValue(),
control.isOwnMessage()));
adjustPadding((int) listView.getWidth(), control.isOwnMessage());
if (control.isOwnMessage()) setAlignment(Pos.CENTER_RIGHT);
else setAlignment(Pos.CENTER_LEFT);
if (control.isOwnMessage())
setAlignment(Pos.CENTER_RIGHT);
else
setAlignment(Pos.CENTER_LEFT);
return control;
}
private void adjustPadding(int listWidth, boolean ownMessage) {
int padding = 10 + Math.max((listWidth - 1000) / 2, 0);
final int padding = 10 + Math.max((listWidth - 1000) / 2, 0);
setPadding(ownMessage ? new Insets(3, padding, 3, 0) : new Insets(3, 0, 3, padding));
}
}

View File

@ -1,6 +1,5 @@
/**
* This package contains custom list cells that are used to display certain
* things.
* This package contains custom list cells that are used to display certain things.
*
* @author Leon Hofmeister
* @author Kai S. K. Engelbart

View File

@ -7,8 +7,8 @@ import javafx.scene.input.InputEvent;
import envoy.event.IssueProposal;
/**
* This class offers the option for users to submit a bug report. Only the title
* of a bug is needed to be sent.
* This class offers the option for users to submit a bug report. Only the title of a bug is needed
* to be sent.
*
* @author Leon Hofmeister
* @since Envoy Client v0.2-beta
@ -17,12 +17,15 @@ public final class BugReportPane extends OnlineOnlySettingsPane {
private final Label titleLabel = new Label("Suggest a title for the bug:");
private final TextField titleTextField = new TextField();
private final Label pleaseExplainLabel = new Label("Paste here the log of what went wrong and/ or explain what went wrong:");
private final Label pleaseExplainLabel =
new Label("Paste here the log of what went wrong and/ or explain what went wrong:");
private final TextArea errorDetailArea = new TextArea();
private final CheckBox showUsernameInBugReport = new CheckBox("Show your username in the bug report?");
private final CheckBox showUsernameInBugReport =
new CheckBox("Show your username in the bug report?");
private final Button submitReportButton = new Button("Submit report");
private final EventHandler<? super InputEvent> inputEventHandler = e -> submitReportButton.setDisable(titleTextField.getText().isBlank());
private final EventHandler<? super InputEvent> inputEventHandler =
e -> submitReportButton.setDisable(titleTextField.getText().isBlank());
/**
* Creates a new {@code BugReportPane}.
@ -58,8 +61,10 @@ public final class BugReportPane extends OnlineOnlySettingsPane {
// Displaying the submitReportButton
submitReportButton.setDisable(true);
submitReportButton.setOnAction(e -> {
String title = titleTextField.getText(), description = errorDetailArea.getText();
client.send(showUsernameInBugReport.isSelected() ? new IssueProposal(title, description, true) : new IssueProposal(title, description, client.getSender().getName(), true));
final String title = titleTextField.getText(), description = errorDetailArea.getText();
client.send(showUsernameInBugReport.isSelected()
? new IssueProposal(title, description, true)
: new IssueProposal(title, description, client.getSender().getName(), true));
});
getChildren().add(submitReportButton);
}

View File

@ -24,18 +24,22 @@ public final class DownloadSettingsPane extends SettingsPane {
setPadding(new Insets(15));
// Checkbox to disable asking
final var checkBox = new CheckBox(settings.getItems().get("autoSaveDownloads").getUserFriendlyName());
final var checkBox =
new CheckBox(settings.getItems().get("autoSaveDownloads").getUserFriendlyName());
checkBox.setSelected(settings.isDownloadSavedWithoutAsking());
checkBox.setTooltip(new Tooltip("Determines whether a \"Select save location\" - dialogue will be shown when saving attachments."));
checkBox.setTooltip(new Tooltip(
"Determines whether a \"Select save location\" - dialogue will be shown when saving attachments."));
checkBox.setOnAction(e -> settings.setDownloadSavedWithoutAsking(checkBox.isSelected()));
getChildren().add(checkBox);
// Displaying the default path to save to
final var pathLabel = new Label(settings.getItems().get("downloadLocation").getDescription() + ":");
final var pathLabel =
new Label(settings.getItems().get("downloadLocation").getDescription() + ":");
pathLabel.setWrapText(true);
getChildren().add(pathLabel);
final var hbox = new HBox(20);
Tooltip.install(hbox, new Tooltip("Determines the location where attachments will be saved to."));
Tooltip.install(hbox,
new Tooltip("Determines the location where attachments will be saved to."));
final var currentPath = new Label(settings.getDownloadLocation().getAbsolutePath());
hbox.getChildren().add(currentPath);
@ -45,7 +49,8 @@ public final class DownloadSettingsPane extends SettingsPane {
final var directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Select the directory where attachments should be saved to");
directoryChooser.setInitialDirectory(settings.getDownloadLocation());
final var selectedDirectory = directoryChooser.showDialog(context.getSceneContext().getStage());
final var selectedDirectory =
directoryChooser.showDialog(context.getSceneContext().getStage());
if (selectedDirectory != null) {
currentPath.setText(selectedDirectory.getAbsolutePath());

View File

@ -2,13 +2,14 @@ package envoy.client.ui.settings;
import javafx.scene.control.*;
import dev.kske.eventbus.EventBus;
import envoy.data.User.UserStatus;
import envoy.client.data.SettingsItem;
import envoy.client.event.ThemeChangeEvent;
import envoy.client.ui.StatusTrayIcon;
import envoy.client.util.UserUtil;
import envoy.data.User.UserStatus;
import dev.kske.eventbus.EventBus;
/**
* @author Kai S. K. Engelbart
@ -27,22 +28,28 @@ public final class GeneralSettingsPane extends SettingsPane {
// Add hide on close if supported
if (StatusTrayIcon.isSupported()) {
final var hideOnCloseCheckbox = new SettingsCheckbox((SettingsItem<Boolean>) settingsItems.get("hideOnClose"));
final var hideOnCloseTooltip = new Tooltip("If selected, Envoy will still be present in the task bar when closed.");
final var hideOnCloseCheckbox =
new SettingsCheckbox((SettingsItem<Boolean>) settingsItems.get("hideOnClose"));
final var hideOnCloseTooltip =
new Tooltip(
"If selected, Envoy will still be present in the task bar when closed.");
hideOnCloseTooltip.setWrapText(true);
hideOnCloseCheckbox.setTooltip(hideOnCloseTooltip);
getChildren().add(hideOnCloseCheckbox);
}
final var enterToSendCheckbox = new SettingsCheckbox((SettingsItem<Boolean>) settingsItems.get("enterToSend"));
final var enterToSendCheckbox =
new SettingsCheckbox((SettingsItem<Boolean>) settingsItems.get("enterToSend"));
final var enterToSendTooltip = new Tooltip(
"When selected, messages can be sent pressing \"Enter\". A line break can be inserted by pressing \"Ctrl\" + \"Enter\". Else it will be the other way around.");
"When selected, messages can be sent pressing \"Enter\". A line break can be inserted by pressing \"Ctrl\" + \"Enter\". Else it will be the other way around.");
enterToSendTooltip.setWrapText(true);
enterToSendCheckbox.setTooltip(enterToSendTooltip);
getChildren().add(enterToSendCheckbox);
final var askForConfirmationCheckbox = new SettingsCheckbox((SettingsItem<Boolean>) settingsItems.get("askForConfirmation"));
final var askForConfirmationTooltip = new Tooltip("When selected, nothing will prompt a confirmation dialog");
final var askForConfirmationCheckbox =
new SettingsCheckbox((SettingsItem<Boolean>) settingsItems.get("askForConfirmation"));
final var askForConfirmationTooltip =
new Tooltip("When selected, nothing will prompt a confirmation dialog");
askForConfirmationTooltip.setWrapText(true);
askForConfirmationCheckbox.setTooltip(askForConfirmationTooltip);
getChildren().add(askForConfirmationCheckbox);
@ -50,9 +57,13 @@ public final class GeneralSettingsPane extends SettingsPane {
final var combobox = new ComboBox<String>();
combobox.getItems().add("dark");
combobox.getItems().add("light");
combobox.setTooltip(new Tooltip("Determines the current theme Envoy will be displayed in."));
combobox
.setTooltip(new Tooltip("Determines the current theme Envoy will be displayed in."));
combobox.setValue(settings.getCurrentTheme());
combobox.setOnAction(e -> { settings.setCurrentTheme(combobox.getValue()); EventBus.getInstance().dispatch(new ThemeChangeEvent()); });
combobox.setOnAction(e -> {
settings.setCurrentTheme(combobox.getValue());
EventBus.getInstance().dispatch(new ThemeChangeEvent());
});
getChildren().add(combobox);
final var statusComboBox = new ComboBox<UserStatus>();
@ -64,7 +75,9 @@ public final class GeneralSettingsPane extends SettingsPane {
final var logoutButton = new Button("Logout");
logoutButton.setOnAction(e -> UserUtil.logout());
final var logoutTooltip = new Tooltip("Brings you back to the login screen and removes \"remember me\" status from this account");
final var logoutTooltip =
new Tooltip(
"Brings you back to the login screen and removes \"remember me\" status from this account");
logoutTooltip.setWrapText(true);
logoutButton.setTooltip(logoutTooltip);
getChildren().add(logoutButton);

View File

@ -8,10 +8,10 @@ import javafx.scene.paint.Color;
import envoy.client.net.Client;
/**
* Inheriting from this class signifies that options should only be available if
* the {@link envoy.data.User} is currently online. If the user is currently
* offline, all {@link javafx.scene.Node} variables will be disabled and a
* {@link Tooltip} will be displayed for the whole node.
* Inheriting from this class signifies that options should only be available if the
* {@link envoy.data.User} is currently online. If the user is currently offline, all
* {@link javafx.scene.Node} variables will be disabled and a {@link Tooltip} will be displayed for
* the whole node.
*
* @author Leon Hofmeister
* @author Kai S. K. Engelbart
@ -21,7 +21,8 @@ public abstract class OnlineOnlySettingsPane extends SettingsPane {
protected final Client client = context.getClient();
private final Tooltip beOnlineReminder = new Tooltip("You need to be online to modify your account.");
private final Tooltip beOnlineReminder =
new Tooltip("You need to be online to modify your account.");
/**
* @param title the title of this pane
@ -33,14 +34,17 @@ public abstract class OnlineOnlySettingsPane extends SettingsPane {
setDisable(!client.isOnline());
if (!client.isOnline()) {
final var infoLabel = new Label("You shall not pass!\n(... Unless you would happen to be online)");
final var infoLabel =
new Label("You shall not pass!\n(... Unless you would happen to be online)");
infoLabel.setId("info-label-warning");
infoLabel.setWrapText(true);
getChildren().add(infoLabel);
setBackground(new Background(new BackgroundFill(Color.grayRgb(100, 0.3), CornerRadii.EMPTY, Insets.EMPTY)));
setBackground(new Background(new BackgroundFill(Color.grayRgb(100, 0.3),
CornerRadii.EMPTY, Insets.EMPTY)));
Tooltip.install(this, beOnlineReminder);
} else Tooltip.uninstall(this, beOnlineReminder);
} else
Tooltip.uninstall(this, beOnlineReminder);
}
/**
@ -49,5 +53,7 @@ public abstract class OnlineOnlySettingsPane extends SettingsPane {
* @param text the text to display
* @since Envoy Client v0.2-beta
*/
protected void setToolTipText(String text) { beOnlineReminder.setText(text); }
protected void setToolTipText(String text) {
beOnlineReminder.setText(text);
}
}

View File

@ -15,7 +15,9 @@ public abstract class SettingsPane extends VBox {
protected static final Settings settings = Settings.getInstance();
protected static final Context context = Context.getInstance();
protected SettingsPane(String title) { this.title = title; }
protected SettingsPane(String title) {
this.title = title;
}
/**
* @return the title of this settings pane

View File

@ -14,12 +14,13 @@ import javafx.scene.input.InputEvent;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;
import envoy.client.ui.control.ProfilePicImageView;
import envoy.client.util.IconUtil;
import dev.kske.eventbus.EventBus;
import envoy.event.*;
import envoy.util.*;
import dev.kske.eventbus.EventBus;
import envoy.client.ui.control.ProfilePicImageView;
import envoy.client.util.IconUtil;
/**
* @author Leon Hofmeister
@ -58,12 +59,15 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
profilePic.setFitWidth(60);
profilePic.setFitHeight(60);
profilePic.setOnMouseClicked(e -> {
if (!client.isOnline()) return;
if (!client.isOnline())
return;
final var pictureChooser = new FileChooser();
pictureChooser.setTitle("Select a new profile pic");
pictureChooser.setInitialDirectory(new File(System.getProperty("user.home")));
pictureChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg", "*.bmp", "*.gif"));
pictureChooser.getExtensionFilters()
.add(new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg",
"*.bmp", "*.gif"));
final var file = pictureChooser.showOpenDialog(context.getSceneContext().getStage());
@ -72,7 +76,8 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
// Check max file size
// TODO: Move to config
if (file.length() > 5E6) {
new Alert(AlertType.WARNING, "The selected file exceeds the size limit of 5MB!").showAndWait();
new Alert(AlertType.WARNING,
"The selected file exceeds the size limit of 5MB!").showAndWait();
return;
}
@ -92,8 +97,8 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
newUsername = username;
usernameTextField.setText(username);
final EventHandler<? super InputEvent> textChanged = e -> {
newUsername = usernameTextField.getText();
usernameChanged = newUsername != username;
newUsername = usernameTextField.getText();
usernameChanged = newUsername != username;
};
usernameTextField.setOnInputMethodTextChanged(textChanged);
usernameTextField.setOnKeyTyped(textChanged);
@ -102,14 +107,22 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
// "Displaying" the password change mechanism
final HBox[] passwordHBoxes = { new HBox(), new HBox(), new HBox() };
final Label[] passwordLabels = { new Label("Enter current password:"), new Label("Enter new password:"),
final Label[] passwordLabels =
{ new Label("Enter current password:"), new Label("Enter new password:"),
new Label("Repeat new password:") };
final PasswordField[] passwordFields = { currentPasswordField, newPasswordField, repeatNewPasswordField };
final PasswordField[] passwordFields =
{ currentPasswordField, newPasswordField, repeatNewPasswordField };
final EventHandler<? super InputEvent> passwordEntered = e -> {
newPassword = newPasswordField.getText();
validPassword = newPassword.equals(repeatNewPasswordField.getText())
&& !newPasswordField.getText().isBlank();
newPassword =
newPasswordField.getText();
validPassword =
newPassword.equals(
repeatNewPasswordField
.getText())
&& !newPasswordField
.getText()
.isBlank();
};
newPasswordField.setOnInputMethodTextChanged(passwordEntered);
newPasswordField.setOnKeyTyped(passwordEntered);
@ -125,7 +138,8 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
}
// Displaying the save button
saveButton.setOnAction(e -> save(client.getSender().getID(), currentPasswordField.getText()));
saveButton.setOnAction(e -> save(client.getSender().getID(),
currentPasswordField.getText()));
saveButton.setAlignment(Pos.BOTTOM_RIGHT);
getChildren().add(saveButton);
}
@ -156,7 +170,9 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
} else if (!validContactName) {
final var alert = new Alert(AlertType.ERROR);
alert.setTitle("Invalid username");
alert.setContentText("The entered username does not conform with the naming limitations: " + Bounds.CONTACT_NAME_PATTERN);
alert.setContentText(
"The entered username does not conform with the naming limitations: "
+ Bounds.CONTACT_NAME_PATTERN);
alert.showAndWait();
logger.log(Level.INFO, "An invalid username was requested.");
return;

View File

@ -1,6 +1,5 @@
/**
* This package contains classes used for representing the settings
* visually.
* This package contains classes used for representing the settings visually.
*
* @author Leon Hofmeister
* @author Kai S. K. Engelbart

View File

@ -9,12 +9,12 @@ import javax.imageio.ImageIO;
import javafx.scene.image.Image;
import envoy.client.data.Settings;
import envoy.util.EnvoyLog;
import envoy.client.data.Settings;
/**
* Provides static utility methods for loading icons from the resource
* folder.
* Provides static utility methods for loading icons from the resource folder.
*
* @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta
@ -34,7 +34,10 @@ public final class IconUtil {
* @return the loaded image
* @since Envoy Client v0.1-beta
*/
public static Image load(String path) { return cache.computeIfAbsent(path, p -> new Image(IconUtil.class.getResource(p).toExternalForm())); }
public static Image load(String path) {
return cache.computeIfAbsent(path, p -> new Image(IconUtil.class.getResource(p)
.toExternalForm()));
}
/**
* Loads an image from the resource folder and scales it to the given size.
@ -45,12 +48,14 @@ public final class IconUtil {
* @since Envoy Client v0.1-beta
*/
public static Image load(String path, int size) {
return scaledCache.computeIfAbsent(path + size, p -> new Image(IconUtil.class.getResource(path).toExternalForm(), size, size, true, true));
return scaledCache.computeIfAbsent(path + size,
p -> new Image(IconUtil.class.getResource(path)
.toExternalForm(),
size, size, true, true));
}
/**
* Loads a {@code .png} image from the sub-folder {@code /icons/} of the
* resource folder.
* Loads a {@code .png} image from the sub-folder {@code /icons/} of the resource folder.
* <p>
* The suffix {@code .png} is automatically appended.
*
@ -60,11 +65,13 @@ public final class IconUtil {
* @apiNote let's load a sample image {@code /icons/abc.png}.<br>
* To do that, we only have to call {@code IconUtil.loadIcon("abc")}
*/
public static Image loadIcon(String name) { return load("/icons/" + name + ".png"); }
public static Image loadIcon(String name) {
return load("/icons/" + name + ".png");
}
/**
* Loads a {@code .png} image from the sub-folder {@code /icons/} of the
* resource folder and scales it to the given size.<br>
* Loads a {@code .png} image from the sub-folder {@code /icons/} of the resource folder and
* scales it to the given size.<br>
* The suffix {@code .png} is automatically appended.
*
* @param name the image name without the .png suffix
@ -72,20 +79,19 @@ public final class IconUtil {
* @return the loaded image
* @since Envoy Client v0.1-beta
* @apiNote let's load a sample image {@code /icons/abc.png} in size 16.<br>
* To do that, we only have to call
* {@code IconUtil.loadIcon("abc", 16)}
* To do that, we only have to call {@code IconUtil.loadIcon("abc", 16)}
*/
public static Image loadIcon(String name, int size) { return load("/icons/" + name + ".png", size); }
public static Image loadIcon(String name, int size) {
return load("/icons/" + name + ".png", size);
}
/**
* Loads a {@code .png} image whose design depends on the currently active theme
* from the sub-folder {@code /icons/dark/} or {@code /icons/light/} of the
* resource folder.
* Loads a {@code .png} image whose design depends on the currently active theme from the
* sub-folder {@code /icons/dark/} or {@code /icons/light/} of the resource folder.
* <p>
* The suffix {@code .png} is automatically appended.
*
* @param name the image name without the "black" or "white" suffix and without
* the .png suffix
* @param name the image name without the "black" or "white" suffix and without the .png suffix
* @return the loaded image
* @since Envoy Client v0.1-beta
* @apiNote let's take two sample images {@code /icons/dark/abc.png} and
@ -93,12 +99,14 @@ public final class IconUtil {
* To do that theme sensitive, we only have to call
* {@code IconUtil.loadIconThemeSensitive("abc")}
*/
public static Image loadIconThemeSensitive(String name) { return loadIcon(themeSpecificSubFolder() + name); }
public static Image loadIconThemeSensitive(String name) {
return loadIcon(themeSpecificSubFolder() + name);
}
/**
* Loads a {@code .png} image whose design depends on the currently active theme
* from the sub-folder {@code /icons/dark/} or {@code /icons/light/} of the
* resource folder and scales it to the given size.
* Loads a {@code .png} image whose design depends on the currently active theme from the
* sub-folder {@code /icons/dark/} or {@code /icons/light/} of the resource folder and scales it
* to the given size.
* <p>
* The suffix {@code .png} is automatically appended.
*
@ -111,20 +119,19 @@ public final class IconUtil {
* To do that theme sensitive, we only have to call
* {@code IconUtil.loadIconThemeSensitive("abc", 16)}
*/
public static Image loadIconThemeSensitive(String name, int size) { return loadIcon(themeSpecificSubFolder() + name, size); }
public static Image loadIconThemeSensitive(String name, int size) {
return loadIcon(themeSpecificSubFolder() + name, size);
}
/**
*
* Loads images specified by an enum. The images have to be named like the
* lowercase enum constants with {@code .png} extension and be located inside a
* folder with the lowercase name of the enum, which must be contained inside
* the {@code /icons/} folder.
* Loads images specified by an enum. The images have to be named like the lowercase enum
* constants with {@code .png} extension and be located inside a folder with the lowercase name
* of the enum, which must be contained inside the {@code /icons/} folder.
*
* @param <T> the enum that specifies the images to load
* @param enumClass the class of the enum
* @param size the size to scale the images to
* @return a map containing the loaded images with the corresponding enum
* constants as keys
* @return a map containing the loaded images with the corresponding enum constants as keys
* @since Envoy Client v0.1-beta
*/
public static <T extends Enum<T>> EnumMap<T, Image> loadByEnum(Class<T> enumClass, int size) {
@ -146,23 +153,27 @@ public final class IconUtil {
return awtCache.computeIfAbsent(path, p -> {
try {
return ImageIO.read(IconUtil.class.getResource(path));
} catch (IOException e) {
EnvoyLog.getLogger(IconUtil.class).log(Level.WARNING, String.format("Could not load image at path %s: ", path), e);
} catch (final IOException e) {
EnvoyLog.getLogger(IconUtil.class)
.log(Level.WARNING,
String.format("Could not load image at path %s: ", path), e);
return null;
}
});
}
/**
* This method should be called if the display of an image depends upon the
* currently active theme.<br>
* In case of a default theme, the string returned will be
* ({@code dark/} or {@code light/}), otherwise it will be empty.
* This method should be called if the display of an image depends upon the currently active
* theme.<br>
* In case of a default theme, the string returned will be ({@code dark/} or {@code light/}),
* otherwise it will be empty.
*
* @return the theme specific folder
* @since Envoy Client v0.1-beta
*/
private static String themeSpecificSubFolder() {
return Settings.getInstance().isUsingDefaultTheme() ? Settings.getInstance().getCurrentTheme() + "/" : "";
return Settings.getInstance().isUsingDefaultTheme()
? Settings.getInstance().getCurrentTheme() + "/"
: "";
}
}

View File

@ -7,13 +7,14 @@ import java.util.logging.*;
import javafx.stage.FileChooser;
import envoy.client.data.*;
import envoy.client.event.MessageDeletion;
import envoy.client.ui.controller.ChatScene;
import dev.kske.eventbus.EventBus;
import envoy.data.Message;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.EventBus;
import envoy.client.data.*;
import envoy.client.event.MessageDeletion;
import envoy.client.ui.controller.ChatScene;
/**
* Contains methods that are commonly used for {@link Message}s.
@ -34,8 +35,10 @@ public class MessageUtil {
* @since Envoy Client v0.3-beta
*/
public static void copyMessageText(Message message) {
logger.log(Level.FINEST, "A copy of message text \"" + message.getText() + "\" was requested");
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(message.getText()), null);
logger.log(Level.FINEST,
"A copy of message text \"" + message.getText() + "\" was requested");
Toolkit.getDefaultToolkit().getSystemClipboard()
.setContents(new StringSelection(message.getText()), null);
}
/**
@ -46,8 +49,10 @@ public class MessageUtil {
*/
public static void deleteMessage(Message message) {
final var messageDeletionEvent = new MessageDeletion(message.getID());
final var controller = Context.getInstance().getSceneContext().getController();
if (controller instanceof ChatScene) ((ChatScene) controller).clearMessageSelection();
final var controller =
Context.getInstance().getSceneContext().getController();
if (controller instanceof ChatScene)
((ChatScene) controller).clearMessageSelection();
// Removing the message locally
EventBus.getInstance().dispatch(messageDeletionEvent);
@ -56,22 +61,24 @@ public class MessageUtil {
}
/**
* Forwards the given message.
* Currently not implemented.
* Forwards the given message. Currently not implemented.
*
* @param message the message to forward
* @since Envoy Client v0.3-beta
*/
public static void forwardMessage(Message message) { logger.log(Level.FINEST, "Message forwarding was requested for " + message); }
public static void forwardMessage(Message message) {
logger.log(Level.FINEST, "Message forwarding was requested for " + message);
}
/**
* Quotes the given message.
* Currently not implemented.
* Quotes the given message. Currently not implemented.
*
* @param message the message to quote
* @since Envoy Client v0.3-beta
*/
public static void quoteMessage(Message message) { logger.log(Level.FINEST, "Message quotation was requested for " + message); }
public static void quoteMessage(Message message) {
logger.log(Level.FINEST, "Message quotation was requested for " + message);
}
/**
* Saves the attachment of a message, if present.
@ -81,7 +88,8 @@ public class MessageUtil {
* @since Envoy Client v0.3-beta
*/
public static void saveAttachment(Message message) {
if (!message.hasAttachment()) throw new IllegalArgumentException("Cannot save a non-existing attachment");
if (!message.hasAttachment())
throw new IllegalArgumentException("Cannot save a non-existing attachment");
File file;
final var fileName = message.getAttachment().getName();
final var downloadLocation = Settings.getInstance().getDownloadLocation();
@ -92,14 +100,17 @@ public class MessageUtil {
fileChooser.setInitialFileName(fileName);
fileChooser.setInitialDirectory(downloadLocation);
file = fileChooser.showSaveDialog(Context.getInstance().getSceneContext().getStage());
} else file = new File(downloadLocation, fileName);
} else
file = new File(downloadLocation, fileName);
// A file was selected
if (file != null) try (var fos = new FileOutputStream(file)) {
fos.write(message.getAttachment().getData());
logger.log(Level.FINE, "Attachment of message was saved at " + file.getAbsolutePath());
} catch (final IOException e) {
logger.log(Level.WARNING, "Could not save attachment of " + message + ": ", e);
}
if (file != null)
try (var fos = new FileOutputStream(file)) {
fos.write(message.getAttachment().getData());
logger.log(Level.FINE,
"Attachment of message was saved at " + file.getAbsolutePath());
} catch (final IOException e) {
logger.log(Level.WARNING, "Could not save attachment of " + message + ": ", e);
}
}
}

View File

@ -14,47 +14,44 @@ public final class ReflectionUtil {
private ReflectionUtil() {}
/**
* Gets all declared variable values of the given instance that have the
* specified class.
* Gets all declared variable values of the given instance that have the specified class.
* <p>
* (i.e. can get all {@code JComponents} (Swing) or {@code Nodes} (JavaFX) in a
* GUI class).
* (i.e. can get all {@code JComponents} (Swing) or {@code Nodes} (JavaFX) in a GUI class).
* <p>
* <b>Important: If you are using a module, you first need to declare <br>
* "opens {your_package} to envoy.client.util;" in your module-info.java</b>.
*
* @param <T> the type of the object
* @param <R> the type to return
* @param instance the instance of a given class whose values are to be
* evaluated
* @param instance the instance of a given class whose values are to be evaluated
* @param typeToReturn the type of variable to return
* @return all variables in the given instance that have the requested type
* @throws RuntimeException if an exception occurs
* @since Envoy Client v0.2-beta
*/
public static <T, R> Stream<R> getAllDeclaredVariablesOfTypeAsStream(T instance, Class<R> typeToReturn) {
return Arrays.stream(instance.getClass().getDeclaredFields()).filter(field -> typeToReturn.isAssignableFrom(field.getType())).map(field -> {
try {
field.setAccessible(true);
return typeToReturn.cast(field.get(instance));
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException(e);
}
});
public static <T, R> Stream<R> getAllDeclaredVariablesOfTypeAsStream(T instance,
Class<R> typeToReturn) {
return Arrays.stream(instance.getClass().getDeclaredFields())
.filter(field -> typeToReturn.isAssignableFrom(field.getType()))
.map(field -> {
try {
field.setAccessible(true);
return typeToReturn.cast(field.get(instance));
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException(e);
}
});
}
/**
* Gets all declared variables of the given instance that are children of
* {@code Node}.
* Gets all declared variables of the given instance that are children of {@code Node}.
* <p>
* <b>Important: If you are using a module, you first need to declare <br>
* "opens {your_package} to envoy.client.util;" in your module-info.java</b>.
*
* @param <T> the type of the instance
* @param instance the instance of a given class whose values are to be
* evaluated
* @return all variables of the given object that have the requested type as
* {@code Stream}
* @param instance the instance of a given class whose values are to be evaluated
* @return all variables of the given object that have the requested type as {@code Stream}
* @since Envoy Client v0.2-beta
*/
public static <T> Stream<Node> getAllDeclaredNodeVariablesAsStream(T instance) {
@ -62,15 +59,13 @@ public final class ReflectionUtil {
}
/**
* Gets all declared variables of the given instance that are children of
* {@code Node}<br>
* Gets all declared variables of the given instance that are children of {@code Node}<br>
* <p>
* <b>Important: If you are using a module, you first need to declare <br>
* "opens {your_package} to envoy.client.util;" in your module-info.java</b>.
*
* @param <T> the type of the instance
* @param instance the instance of a given class whose values are to be
* evaluated
* @param instance the instance of a given class whose values are to be evaluated
* @return all variables of the given object that have the requested type
* @since Envoy Client v0.2-beta
*/

View File

@ -5,15 +5,16 @@ import java.util.logging.Level;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import envoy.client.data.Context;
import envoy.client.event.*;
import envoy.client.helper.*;
import envoy.client.ui.SceneContext.SceneInfo;
import dev.kske.eventbus.EventBus;
import envoy.data.User.UserStatus;
import envoy.event.UserStatusChange;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.EventBus;
import envoy.client.data.Context;
import envoy.client.event.*;
import envoy.client.helper.*;
import envoy.client.ui.SceneContext.SceneInfo;
/**
* Contains methods that change something about the currently logged in user.
@ -26,8 +27,7 @@ public final class UserUtil {
private UserUtil() {}
/**
* Logs the current user out and reopens
* {@link envoy.client.ui.controller.LoginScene}.
* Logs the current user out and reopens {@link envoy.client.ui.controller.LoginScene}.
*
* @since Envoy Client v0.2-beta
*/
@ -45,8 +45,7 @@ public final class UserUtil {
}
/**
* Notifies the application that the status of the currently logged in user has
* changed.
* Notifies the application that the status of the currently logged in user has changed.
*
* @param newStatus the new status
* @since Envoy Client v0.3-beta
@ -54,11 +53,15 @@ public final class UserUtil {
public static void changeStatus(UserStatus newStatus) {
// Sending the already active status is a valid action
if (newStatus.equals(Context.getInstance().getLocalDB().getUser().getStatus())) return;
if (newStatus.equals(Context.getInstance().getLocalDB().getUser().getStatus()))
return;
else {
EventBus.getInstance().dispatch(new OwnStatusChange(newStatus));
if (Context.getInstance().getClient().isOnline())
Context.getInstance().getClient().send(new UserStatusChange(Context.getInstance().getLocalDB().getUser().getID(), newStatus));
Context.getInstance().getClient()
.send(new UserStatusChange(Context.getInstance().getLocalDB().getUser()
.getID(),
newStatus));
}
}
}

View File

@ -1,6 +1,5 @@
/**
* This module contains all classes defining the client application of the Envoy
* project.
* This module contains all classes defining the client application of the Envoy project.
*
* @author Kai S. K. Engelbart
* @author Leon Hofmeister

View File

@ -3,8 +3,8 @@ package envoy.data;
import java.io.Serializable;
/**
* This interface should be used for any type supposed to be a {@link Message}
* attachment (i.e. images or sound).
* This interface should be used for any type supposed to be a {@link Message} attachment (i.e.
* images or sound).
*
* @author Leon Hofmeister
* @author Kai S. K. Engelbart

View File

@ -9,15 +9,13 @@ import java.util.stream.Collectors;
import envoy.util.EnvoyLog;
/**
* Manages all application settings that are set during application startup by
* either loading them from the {@link Properties} file (default values)
* {@code client.properties} or parsing them from the command line arguments of
* the application.
* Manages all application settings that are set during application startup by either loading them
* from the {@link Properties} file (default values) {@code client.properties} or parsing them from
* the command line arguments of the application.
* <p>
* All items inside the {@code Config} are supposed to either be supplied over
* default value or over command line argument. Developers that fail to provide
* default values will be greeted with an error message the next time they try
* to start Envoy...
* All items inside the {@code Config} are supposed to either be supplied over default value or over
* command line argument. Developers that fail to provide default values will be greeted with an
* error message the next time they try to start Envoy...
*
* @author Kai S. K. Engelbart
* @since Envoy Common v0.1-beta
@ -44,15 +42,14 @@ public class Config {
*/
private void load(Properties properties) {
items.entrySet().stream().filter(e -> properties.containsKey(e.getKey()))
.forEach(e -> e.getValue().parse(properties.getProperty(e.getKey())));
.forEach(e -> e.getValue().parse(properties.getProperty(e.getKey())));
}
/**
* Parses config items from an array of command line arguments.
*
* @param args the command line arguments to parse
* @throws IllegalStateException if a malformed command line argument has been
* supplied
* @throws IllegalStateException if a malformed command line argument has been supplied
* @since Envoy Common v0.1-beta
*/
private void load(String[] args) {
@ -61,7 +58,8 @@ public class Config {
if (args[i].startsWith("--")) {
if (args[i].length() == 2)
throw new IllegalStateException(
"Malformed command line argument at position " + i + ": " + args[i]);
"Malformed command line argument at position "
+ i + ": " + args[i]);
final String commandLong = args[i].substring(2);
if (item.getCommandLong().equals(commandLong)) {
item.parse(args[++i]);
@ -70,7 +68,8 @@ public class Config {
} else if (args[i].startsWith("-")) {
if (args[i].length() == 1)
throw new IllegalStateException(
"Malformed command line argument at position " + i + ": " + args[i]);
"Malformed command line argument at position "
+ i + ": " + args[i]);
final String commandShort = args[i].substring(1);
if (item.getCommandShort().equals(commandShort)) {
item.parse(args[++i]);
@ -78,34 +77,36 @@ public class Config {
}
} else
throw new IllegalStateException(
"Malformed command line argument at position " + i + ": " + args[i]);
"Malformed command line argument at position "
+ i + ": " + args[i]);
}
/**
* Supplies default values from the given .properties file and parses the
* configuration from an array of command line arguments.
* Supplies default values from the given .properties file and parses the configuration from an
* array of command line arguments.
*
* @param declaringClass the class calling this method
* @param propertiesFilePath the path to where the .properties file can be found
* - will be only the file name if it is located
* directly inside the {@code src/main/resources}
* folder
* @param propertiesFilePath the path to where the .properties file can be found - will be only
* the file name if it is located directly inside the
* {@code src/main/resources} folder
* @param args the command line arguments to parse
* @throws IllegalStateException if this method is getting called again or if a
* malformed command line argument has been
* supplied
* @throws IllegalStateException if this method is getting called again or if a malformed
* command line argument has been supplied
* @since Envoy Common v0.1-beta
*/
public void loadAll(Class<?> declaringClass, String propertiesFilePath, String[] args) {
if (modificationDisabled)
throw new IllegalStateException("Cannot change config after isInitialized has been called");
throw new IllegalStateException(
"Cannot change config after isInitialized has been called");
// Load the defaults from the given .properties file first
final var properties = new Properties();
try {
properties.load(declaringClass.getClassLoader().getResourceAsStream(propertiesFilePath));
properties.load(declaringClass.getClassLoader()
.getResourceAsStream(propertiesFilePath));
} catch (final IOException e) {
EnvoyLog.getLogger(Config.class).log(Level.SEVERE, "An error occurred when reading in the configuration: ",
EnvoyLog.getLogger(Config.class)
.log(Level.SEVERE, "An error occurred when reading in the configuration: ",
e);
}
load(properties);
@ -122,13 +123,14 @@ public class Config {
}
/**
* @throws IllegalStateException if a {@link ConfigItem} has not been
* initialized
* @throws IllegalStateException if a {@link ConfigItem} has not been initialized
* @since Envoy Common v0.1-beta
*/
private void isInitialized() {
String uninitialized = items.values().stream().filter(c -> c.get() == null).map(ConfigItem::getCommandLong).collect(Collectors.joining(", "));
if(!uninitialized.isEmpty())
final String uninitialized =
items.values().stream().filter(c -> c.get() == null).map(ConfigItem::getCommandLong)
.collect(Collectors.joining(", "));
if (!uninitialized.isEmpty())
throw new IllegalStateException("Config items uninitialized: " + uninitialized);
}
@ -148,11 +150,11 @@ public class Config {
* @param <T> the type of the {@link ConfigItem}
* @param commandName the key for this config item as well as its long name
* @param commandShort the abbreviation of this config item
* @param parseFunction the {@code Function<String, T>} that parses the value
* from a string
* @param parseFunction the {@code Function<String, T>} that parses the value from a string
* @since Envoy Common v0.2-beta
*/
protected <T> void put(String commandName, String commandShort, Function<String, T> parseFunction) {
protected <T> void put(String commandName, String commandShort,
Function<String, T> parseFunction) {
items.put(commandName, new ConfigItem<>(commandName, commandShort, parseFunction));
}
@ -160,17 +162,13 @@ public class Config {
* @return the directory in which all local files are saves
* @since Envoy Client v0.2-beta
*/
public File getHomeDirectory() {
return (File) items.get("homeDirectory").get();
}
public File getHomeDirectory() { return (File) items.get("homeDirectory").get(); }
/**
* @return the minimal {@link Level} to log inside the log file
* @since Envoy Client v0.2-beta
*/
public Level getFileLevelBarrier() {
return (Level) items.get("fileLevelBarrier").get();
}
public Level getFileLevelBarrier() { return (Level) items.get("fileLevelBarrier").get(); }
/**
* @return the minimal {@link Level} to log inside the console

View File

@ -3,8 +3,8 @@ package envoy.data;
import java.util.function.Function;
/**
* Contains a single {@link Config} value as well as the corresponding command
* line arguments and its default value.
* Contains a single {@link Config} value as well as the corresponding command line arguments and
* its default value.
* <p>
* All {@code ConfigItem}s are automatically mandatory.
*
@ -24,8 +24,7 @@ public final class ConfigItem<T> {
*
* @param commandLong the long command line argument to set this value
* @param commandShort the short command line argument to set this value
* @param parseFunction the {@code Function<String, T>} that parses the value
* from a string
* @param parseFunction the {@code Function<String, T>} that parses the value from a string
* @since Envoy Common v0.1-beta
*/
public ConfigItem(String commandLong, String commandShort, Function<String, T> parseFunction) {
@ -40,18 +39,18 @@ public final class ConfigItem<T> {
* @param input the string to parse from
* @since Envoy Common v0.1-beta
*/
public void parse(String input) { value = parseFunction.apply(input); }
public void parse(String input) {
value = parseFunction.apply(input);
}
/**
* @return The long command line argument to set the value of this
* {@link ConfigItem}
* @return The long command line argument to set the value of this {@link ConfigItem}
* @since Envoy Common v0.1-beta
*/
public String getCommandLong() { return commandLong; }
/**
* @return The short command line argument to set the value of this
* {@link ConfigItem}
* @return The short command line argument to set the value of this {@link ConfigItem}
* @since Envoy Common v0.1-beta
*/
public String getCommandShort() { return commandShort; }
@ -60,7 +59,9 @@ public final class ConfigItem<T> {
* @return the value of this {@link ConfigItem}
* @since Envoy Common v0.1-beta
*/
public T get() { return value; }
public T get() {
return value;
}
/**
* @param value the value to set

View File

@ -57,19 +57,23 @@ public abstract class Contact implements Serializable {
* @since Envoy Common v0.1-beta
*/
@Override
public final int hashCode() { return Objects.hash(id); }
public final int hashCode() {
return Objects.hash(id);
}
/**
* Tests equality to another object. If that object is a contact as well,
* equality is determined by the ID.
* Tests equality to another object. If that object is a contact as well, equality is determined
* by the ID.
*
* @param obj the object to test for equality to this contact
* @return {code true} if both objects are contacts and have identical IDs
*/
@Override
public final boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Contact)) return false;
if (this == obj)
return true;
if (!(obj instanceof Contact))
return false;
return id == ((Contact) obj).id;
}

View File

@ -18,7 +18,9 @@ public final class Group extends Contact {
* @param name the name of this group
* @since Envoy Common v0.1-beta
*/
public Group(long id, String name) { this(id, name, new HashSet<User>()); }
public Group(long id, String name) {
this(id, name, new HashSet<User>());
}
/**
* Creates an instance of a {@link Group}.
@ -28,14 +30,18 @@ public final class Group extends Contact {
* @param members all members that should be preinitialized
* @since Envoy Common v0.1-beta
*/
public Group(long id, String name, Set<User> members) { super(id, name, members); }
public Group(long id, String name, Set<User> members) {
super(id, name, members);
}
@Override
public String toString() { return String.format("Group[id=%d,name=%s,%d member(s)]", id, name, contacts.size()); }
public String toString() {
return String.format("Group[id=%d,name=%s,%d member(s)]", id, name, contacts.size());
}
private void readObject(ObjectInputStream inputStream) throws Exception {
inputStream.defaultReadObject();
var contacts = Contact.class.getDeclaredField("contacts");
final var contacts = Contact.class.getDeclaredField("contacts");
contacts.setAccessible(true);
contacts.set(this, inputStream.readObject());
}

View File

@ -14,11 +14,9 @@ public final class GroupMessage extends Message {
private static final long serialVersionUID = 1L;
/**
* Initializes a {@link GroupMessage} with values for all of its properties. The
* use
* of this constructor is only intended for the {@link MessageBuilder} class, as
* this class provides {@code null} checks and default values for all
* properties.
* Initializes a {@link GroupMessage} with values for all of its properties. The use of this
* constructor is only intended for the {@link MessageBuilder} class, as this class provides
* {@code null} checks and default values for all properties.
*
* @param id unique ID
* @param senderID the ID of the user who sends the message
@ -28,16 +26,18 @@ public final class GroupMessage extends Message {
* @param readDate the read date of the message
* @param text the text content of the message
* @param attachment the attachment of the message, if present
* @param status the current {@link Message.MessageStatus} of the
* message
* @param status the current {@link Message.MessageStatus} of the message
* @param forwarded whether this message was forwarded
* @param memberStatuses a map of all members and their status according to this
* {@link GroupMessage}
* @since Envoy Common v0.2-beta
*/
GroupMessage(long id, long senderID, long groupID, Instant creationDate, Instant receivedDate, Instant readDate, String text,
Attachment attachment, MessageStatus status, boolean forwarded, Map<Long, MessageStatus> memberStatuses) {
super(id, senderID, groupID, creationDate, receivedDate, readDate, text, attachment, status, forwarded);
GroupMessage(long id, long senderID, long groupID, Instant creationDate, Instant receivedDate,
Instant readDate, String text,
Attachment attachment, MessageStatus status, boolean forwarded,
Map<Long, MessageStatus> memberStatuses) {
super(id, senderID, groupID, creationDate, receivedDate, readDate, text, attachment, status,
forwarded);
this.memberStatuses = memberStatuses;
}

View File

@ -30,20 +30,25 @@ public final class IDGenerator implements IEvent, Serializable {
}
@Override
public String toString() { return String.format("IDGenerator[current=%d,end=%d]", current, end); }
public String toString() {
return String.format("IDGenerator[current=%d,end=%d]", current, end);
}
/**
* @return {@code true} if there are unused IDs remaining
* @since Envoy Common v0.2-alpha
*/
public boolean hasNext() { return current < end; }
public boolean hasNext() {
return current < end;
}
/**
* @return the next ID
* @since Envoy Common v0.2-alpha
*/
public long next() {
if (!hasNext()) throw new IllegalStateException("All IDs have been used");
if (!hasNext())
throw new IllegalStateException("All IDs have been used");
return current++;
}
}

View File

@ -4,11 +4,9 @@ import java.io.Serializable;
import java.time.Instant;
/**
* Contains a {@link User}'s login / registration information as well as the
* client version.
* Contains a {@link User}'s login / registration information as well as the client version.
* <p>
* If the authentication is performed with a token, the token is stored instead
* of the password.
* If the authentication is performed with a token, the token is stored instead of the password.
*
* @author Kai S. K. Engelbart
* @since Envoy Common v0.2-alpha
@ -21,8 +19,9 @@ public final class LoginCredentials implements Serializable {
private static final long serialVersionUID = 4;
private LoginCredentials(String identifier, String password, boolean registration, boolean token, boolean requestToken, String clientVersion,
Instant lastSync) {
private LoginCredentials(String identifier, String password, boolean registration,
boolean token, boolean requestToken, String clientVersion,
Instant lastSync) {
this.identifier = identifier;
this.password = password;
this.registration = registration;
@ -43,8 +42,10 @@ public final class LoginCredentials implements Serializable {
* @return the created login credentials
* @since Envoy Common v0.2-beta
*/
public static LoginCredentials login(String identifier, String password, boolean requestToken, String clientVersion, Instant lastSync) {
return new LoginCredentials(identifier, password, false, false, requestToken, clientVersion, lastSync);
public static LoginCredentials login(String identifier, String password, boolean requestToken,
String clientVersion, Instant lastSync) {
return new LoginCredentials(identifier, password, false, false, requestToken, clientVersion,
lastSync);
}
/**
@ -57,7 +58,8 @@ public final class LoginCredentials implements Serializable {
* @return the created login credentials
* @since Envoy Common v0.2-beta
*/
public static LoginCredentials loginWithToken(String identifier, String token, String clientVersion, Instant lastSync) {
public static LoginCredentials loginWithToken(String identifier, String token,
String clientVersion, Instant lastSync) {
return new LoginCredentials(identifier, token, false, true, false, clientVersion, lastSync);
}
@ -72,19 +74,23 @@ public final class LoginCredentials implements Serializable {
* @return the created login credentials
* @since Envoy Common v0.2-beta
*/
public static LoginCredentials registration(String identifier, String password, boolean requestToken, String clientVersion, Instant lastSync) {
return new LoginCredentials(identifier, password, true, false, requestToken, clientVersion, lastSync);
public static LoginCredentials registration(String identifier, String password,
boolean requestToken, String clientVersion,
Instant lastSync) {
return new LoginCredentials(identifier, password, true, false, requestToken, clientVersion,
lastSync);
}
@Override
public String toString() {
return String.format("LoginCredentials[identifier=%s,registration=%b,token=%b,requestToken=%b,clientVersion=%s,lastSync=%s]",
identifier,
registration,
token,
requestToken,
clientVersion,
lastSync);
return String.format(
"LoginCredentials[identifier=%s,registration=%b,token=%b,requestToken=%b,clientVersion=%s,lastSync=%s]",
identifier,
registration,
token,
requestToken,
clientVersion,
lastSync);
}
/**
@ -100,24 +106,27 @@ public final class LoginCredentials implements Serializable {
public String getPassword() { return password; }
/**
* @return {@code true} if these credentials are used for user registration
* instead of user login
* @return {@code true} if these credentials are used for user registration instead of user
* login
* @since Envoy Common v0.2-alpha
*/
public boolean isRegistration() { return registration; }
/**
* @return {@code true} if these credentials use an authentication token instead
* of a password
* @return {@code true} if these credentials use an authentication token instead of a password
* @since Envoy Common v0.2-beta
*/
public boolean usesToken() { return token; }
public boolean usesToken() {
return token;
}
/**
* @return {@code true} if the server should generate a new authentication token
* @since Envoy Common v0.2-beta
*/
public boolean requestToken() { return requestToken; }
public boolean requestToken() {
return requestToken;
}
/**
* @return the version of the client sending these credentials

View File

@ -6,9 +6,8 @@ import java.time.Instant;
import dev.kske.eventbus.IEvent;
/**
* Represents a unique message with a unique, numeric ID. Further metadata
* includes the sender and recipient {@link User}s, as well as the creation
* date and the current {@link MessageStatus}.<br>
* Represents a unique message with a unique, numeric ID. Further metadata includes the sender and
* recipient {@link User}s, as well as the creation date and the current {@link MessageStatus}.<br>
*
* @author Kai S. K. Engelbart
* @author Leon Hofmeister
@ -56,10 +55,9 @@ public class Message implements Serializable, IEvent {
private static final long serialVersionUID = 2L;
/**
* Initializes a {@link Message} with values for all of its properties. The use
* of this constructor is only intended for the {@link MessageBuilder} class, as
* this class provides {@code null} checks and default values for all
* properties.
* Initializes a {@link Message} with values for all of its properties. The use of this
* constructor is only intended for the {@link MessageBuilder} class, as this class provides
* {@code null} checks and default values for all properties.
*
* @param id unique ID
* @param senderID the ID of the user who sends the message
@ -73,8 +71,9 @@ public class Message implements Serializable, IEvent {
* @param forwarded whether this message was forwarded
* @since Envoy Common v0.2-beta
*/
Message(long id, long senderID, long recipientID, Instant creationDate, Instant receivedDate, Instant readDate, String text,
Attachment attachment, MessageStatus status, boolean forwarded) {
Message(long id, long senderID, long recipientID, Instant creationDate, Instant receivedDate,
Instant readDate, String text,
Attachment attachment, MessageStatus status, boolean forwarded) {
this.id = id;
this.senderID = senderID;
this.recipientID = recipientID;
@ -101,21 +100,23 @@ public class Message implements Serializable, IEvent {
* @since Envoy Common v0.2-alpha
*/
public void nextStatus() {
if (status == MessageStatus.READ) throw new IllegalStateException("Message status READ is already reached");
if (status == MessageStatus.READ)
throw new IllegalStateException("Message status READ is already reached");
status = MessageStatus.values()[status.ordinal() + 1];
}
@Override
public String toString() {
return String.format("Message[id=%d,sender=%s,recipient=%s,date=%s,status=%s,text=%s,forwarded=%b,hasAttachment=%b]",
id,
senderID,
recipientID,
creationDate,
status,
text,
forwarded,
attachment != null);
return String.format(
"Message[id=%d,sender=%s,recipient=%s,date=%s,status=%s,text=%s,forwarded=%b,hasAttachment=%b]",
id,
senderID,
recipientID,
creationDate,
status,
text,
forwarded,
attachment != null);
}
/**
@ -149,8 +150,7 @@ public class Message implements Serializable, IEvent {
public Instant getReceivedDate() { return receivedDate; }
/**
* @param receivedDate the date at which the message has been received by the
* sender
* @param receivedDate the date at which the message has been received by the sender
* @since Envoy Common v0.2-beta
*/
public void setReceivedDate(Instant receivedDate) { this.receivedDate = receivedDate; }
@ -183,7 +183,9 @@ public class Message implements Serializable, IEvent {
* @return {@code true} if an attachment is present
* @since Envoy Common v0.1-beta
*/
public boolean hasAttachment() { return attachment != null; }
public boolean hasAttachment() {
return attachment != null;
}
/**
* @return the current status of this message
@ -196,7 +198,8 @@ public class Message implements Serializable, IEvent {
* @since Envoy Common v0.2-alpha
*/
public void setStatus(MessageStatus status) {
if (status.ordinal() < this.status.ordinal()) throw new IllegalStateException("This message is moving backwards in time");
if (status.ordinal() < this.status.ordinal())
throw new IllegalStateException("This message is moving backwards in time");
this.status = status;
}

View File

@ -25,20 +25,21 @@ public final class MessageBuilder {
private boolean forwarded;
/**
* Creates an instance of {@link MessageBuilder} with all mandatory values
* without defaults for the {@link Message} class.
* Creates an instance of {@link MessageBuilder} with all mandatory values without defaults for
* the {@link Message} class.
*
* @param senderID the ID of the user who sends the {@link Message}
* @param recipientID the ID of the user who receives the {@link Message}
* @param idGenerator the ID generator used to generate a unique {@link Message}
* id
* @param idGenerator the ID generator used to generate a unique {@link Message} id
* @since Envoy Common v0.2-alpha
*/
public MessageBuilder(long senderID, long recipientID, IDGenerator idGenerator) { this(senderID, recipientID, idGenerator.next()); }
public MessageBuilder(long senderID, long recipientID, IDGenerator idGenerator) {
this(senderID, recipientID, idGenerator.next());
}
/**
* Creates an instance of {@link MessageBuilder} with all mandatory values
* without defaults for the {@link Message} class.
* Creates an instance of {@link MessageBuilder} with all mandatory values without defaults for
* the {@link Message} class.
*
* @param senderID the ID of the user who sends the {@link Message}
* @param recipientID the ID of the user who receives the {@link Message}
@ -52,14 +53,12 @@ public final class MessageBuilder {
}
/**
* This constructor transforms a given {@link Message} into a new message for a
* new receiver.
* This constructor transforms a given {@link Message} into a new message for a new receiver.
* This makes it especially useful in the case of forwarding messages.
*
* @param msg the message to copy
* @param recipientID the ID of the user who receives the {@link Message}
* @param iDGenerator the ID generator used to generate a unique {@link Message}
* id
* @param iDGenerator the ID generator used to generate a unique {@link Message} id
* @since Envoy v0.1-beta
*/
public MessageBuilder(Message msg, long recipientID, IDGenerator iDGenerator) {
@ -72,79 +71,69 @@ public final class MessageBuilder {
}
/**
* Creates an instance of {@link Message} with the previously supplied values.
* If a mandatory value is not set, a default value will be used instead:<br>
* Creates an instance of {@link Message} with the previously supplied values. If a mandatory
* value is not set, a default value will be used instead:<br>
* <br>
* {@code date}
* {@code Instant.now()} and {@code null} for {@code receivedDate} and
* {@code readDate}
* <br>
* {@code text}
* {@code ""}
* <br>
* {@code status}
* {@code MessageStatus.WAITING}
* {@code date} {@code Instant.now()} and {@code null} for {@code receivedDate} and
* {@code readDate} <br>
* {@code text} {@code ""} <br>
* {@code status} {@code MessageStatus.WAITING}
*
* @return a new instance of {@link Message}
* @since Envoy Common v0.2-alpha
*/
public Message build() {
supplyDefaults();
return new Message(id, senderID, recipientID, creationDate, receivedDate, readDate, text, attachment, status, forwarded);
return new Message(id, senderID, recipientID, creationDate, receivedDate, readDate, text,
attachment, status, forwarded);
}
/**
* Creates an instance of {@link GroupMessage} with the previously supplied
* values. <br>
* 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>
* {@code time stamp}
* {@code Instant.now()}
* <br>
* {@code text}
* {@code ""}
* If a mandatory value is not set, a default value will be used instead:<br>
* <br>
* {@code time stamp} {@code Instant.now()} <br>
* {@code text} {@code ""} <br>
*
* @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
* @return a new instance of {@link GroupMessage}
* @since Envoy Common v0.2-alpha
*/
public GroupMessage buildGroupMessage(Group group) {
final var memberStatuses = new HashMap<Long, Message.MessageStatus>();
group.getContacts().forEach(user -> memberStatuses.put(user.getID(), MessageStatus.WAITING));
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
* instead:<br>
* Creates an instance of {@link GroupMessage} with the previously supplied values. If a
* mandatory value is not set, a default value will be used instead:<br>
* <br>
* {@code time stamp}
* {@code Instant.now()}
* <br>
* {@code text}
* {@code ""}
* {@code time stamp} {@code Instant.now()} <br>
* {@code text} {@code ""}
*
* @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.1-beta
*/
public GroupMessage buildGroupMessage(Group group, Map<Long, MessageStatus> memberStatuses) {
if (group == null || memberStatuses == null) throw new NullPointerException();
if (group == null || memberStatuses == null)
throw new NullPointerException();
supplyDefaults();
return new GroupMessage(id, senderID, recipientID, creationDate, receivedDate, readDate, text, attachment, status, forwarded, memberStatuses);
return new GroupMessage(id, senderID, recipientID, creationDate, receivedDate, readDate,
text, attachment, status, forwarded, memberStatuses);
}
private void supplyDefaults() {
if (creationDate == null) creationDate = Instant.now();
if (text == null) text = "";
if (status == null) status = MessageStatus.WAITING;
if (creationDate == null)
creationDate = Instant.now();
if (text == null)
text = "";
if (status == null)
status = MessageStatus.WAITING;
}
/**
@ -188,8 +177,7 @@ public final class MessageBuilder {
}
/**
* @param attachment the {@link Attachment} of the {@link Message} to
* create
* @param attachment the {@link Attachment} of the {@link Message} to create
* @return this {@link MessageBuilder}
* @since Envoy Common v0.2-alpha
*/

View File

@ -4,8 +4,7 @@ import java.io.*;
import java.util.*;
/**
* Represents a unique user with a unique, numeric ID, a name and a current
* {@link UserStatus}.<br>
* Represents a unique user with a unique, numeric ID, a name and a current {@link UserStatus}.<br>
*
* @author Kai S. K. Engelbart
* @since Envoy Common v0.2-alpha
@ -34,8 +33,7 @@ public final class User extends Contact {
ONLINE,
/**
* select this, if a user is online but unavailable at the moment (sudden
* interruption)
* select this, if a user is online but unavailable at the moment (sudden interruption)
*/
AWAY,
@ -52,8 +50,7 @@ public final class User extends Contact {
/**
* Initializes a {@link User}. <br>
* The {@link UserStatus} is set to {@link UserStatus#ONLINE}.
* No contacts are initialized.
* The {@link UserStatus} is set to {@link UserStatus#ONLINE}. No contacts are initialized.
*
* @param id unique ID
* @param name user name
@ -94,7 +91,8 @@ public final class User extends Contact {
@Override
public String toString() {
return String.format("User[id=%d,name=%s,status=%s", id, name, status) + (contacts.isEmpty() ? "]" : "," + contacts.size() + " contact(s)]");
return String.format("User[id=%d,name=%s,status=%s", id, name, status)
+ (contacts.isEmpty() ? "]" : "," + contacts.size() + " contact(s)]");
}
/**
@ -111,7 +109,7 @@ public final class User extends Contact {
private void readObject(ObjectInputStream inputStream) throws Exception {
inputStream.defaultReadObject();
var contacts = Contact.class.getDeclaredField("contacts");
final var contacts = Contact.class.getDeclaredField("contacts");
contacts.setAccessible(true);
contacts.set(this, inputStream.readObject());
}
@ -119,15 +117,18 @@ public final class User extends Contact {
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);
getContacts().stream().filter(User.class::isInstance).map(User.class::cast)
.forEach(user -> user.serializeContacts = false);
outputStream.writeObject(getContacts());
} else outputStream.writeObject(new HashSet<>());
} else
outputStream.writeObject(new HashSet<>());
}
/**
* @param serializeContacts whether the contacts of this {@link User} should be
* serialized
* @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; }
public void serializeContacts(boolean serializeContacts) {
this.serializeContacts = serializeContacts;
}
}

View File

@ -1,6 +1,5 @@
/**
* This package contains all data objects that are used both by Envoy Client and
* by Envoy Server.
* This package contains all data objects that are used both by Envoy Client and by Envoy Server.
*
* @author Leon Hofmeister
* @author Maximilian K&auml;fer

View File

@ -3,8 +3,7 @@ package envoy.event;
/**
* This enum declares all modification possibilities for a given container.
* <p>
* These can be: {@link ElementOperation#ADD} or
* {@link ElementOperation#REMOVE}.
* These can be: {@link ElementOperation#ADD} or {@link ElementOperation#REMOVE}.
*
* @author Leon Hofmeister
* @since Envoy Common v0.1-beta
@ -12,14 +11,12 @@ package envoy.event;
public enum ElementOperation {
/**
* Select this element, if the given element should be added to the given
* container.
* Select this element, if the given element should be added to the given container.
*/
ADD,
/**
* Select this element, if the given element should be removed from the given
* container.
* Select this element, if the given element should be removed from the given container.
*/
REMOVE
}

View File

@ -5,9 +5,9 @@ import java.io.Serializable;
import dev.kske.eventbus.IEvent;
/**
* This class serves as a convenience base class for all events. It implements
* the {@link IEvent} interface and provides a generic value. For events without
* a value there also is {@link envoy.event.Event.Valueless}.
* This class serves as a convenience base class for all events. It implements the {@link IEvent}
* interface and provides a generic value. For events without a value there also is
* {@link envoy.event.Event.Valueless}.
*
* @author Kai S. K. Engelbart
* @param <T> the type of the Event
@ -19,15 +19,21 @@ public abstract class Event<T> implements IEvent, Serializable {
private static final long serialVersionUID = 0L;
protected Event(T value) { this.value = value; }
protected Event(T value) {
this.value = value;
}
/**
* @return the data associated with this event
*/
public T get() { return value; }
public T get() {
return value;
}
@Override
public String toString() { return String.format("%s[value=%s]", this.getClass().getSimpleName(), value); }
public String toString() {
return String.format("%s[value=%s]", this.getClass().getSimpleName(), value);
}
/**
* Serves as a super class for events that do not carry a value.
@ -39,9 +45,13 @@ public abstract class Event<T> implements IEvent, Serializable {
private static final long serialVersionUID = 0L;
protected Valueless() { super(null); }
protected Valueless() {
super(null);
}
@Override
public String toString() { return this.getClass().getSimpleName(); }
public String toString() {
return this.getClass().getSimpleName();
}
}
}

View File

@ -18,9 +18,8 @@ public final class GroupCreation extends Event<String> {
/**
* @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)
* @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 GroupCreation(String value, Set<Long> initialMemberIDs) {
@ -29,8 +28,8 @@ public final class GroupCreation extends Event<String> {
}
/**
* @return the IDs of all {@link User}s that are members from the beginning
* (excluding the creator of this group)
* @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

@ -1,8 +1,8 @@
package envoy.event;
/**
* Used to communicate with a client that his request to create a group might
* have been rejected as it might be disabled on his current server.
* Used to communicate with a client that his request to create a group might have been rejected as
* it might be disabled on his current server.
*
* @author Leon Hofmeister
* @since Envoy Common v0.2-beta
@ -17,5 +17,7 @@ public class GroupCreationResult extends Event<Boolean> {
* @param success whether the GroupCreation sent before was successful
* @since Envoy Common v0.2-beta
*/
public GroupCreationResult(boolean success) { super(success); }
public GroupCreationResult(boolean success) {
super(success);
}
}

View File

@ -20,8 +20,7 @@ public final class GroupMessageStatusChange extends MessageStatusChange {
*
* @param id the ID of the {@link GroupMessage} this event is related to
* @param status the status of this specific members {@link GroupMessage}
* @param date the date at which the MessageStatus change occurred for
* this specific member
* @param date the date at which the MessageStatus change occurred for this specific member
* @param memberID the ID of the group member that caused the status change
* @since Envoy Common v0.2-beta
*/
@ -37,5 +36,8 @@ public final class GroupMessageStatusChange extends MessageStatusChange {
public long getMemberID() { return memberID; }
@Override
public String toString() { return String.format("GroupMessageStatusChange[meta=%s,memberID=%d]", super.toString(), memberID); }
public String toString() {
return String.format("GroupMessageStatusChange[meta=%s,memberID=%d]", super.toString(),
memberID);
}
}

View File

@ -5,11 +5,9 @@ import static envoy.event.ElementOperation.*;
import envoy.data.*;
/**
* This event is used to communicate changes in the group size between client
* and server.
* This event is used to communicate changes in the group size between client and server.
* <p>
* Possible actions are adding or removing certain {@link User}s to or from a
* certain {@link Group}.
* Possible actions are adding or removing certain {@link User}s to or from a certain {@link Group}.
*
* @author Leon Hofmeister
* @since Envoy Common v0.1-beta
@ -22,8 +20,7 @@ public final class GroupResize extends Event<User> {
private static final long serialVersionUID = 0L;
/**
* Initializes a {@link GroupResize} through a Contact where the name has
* already been set.
* Initializes a {@link GroupResize} through a Contact where the name has already been set.
*
* @param user the {@link User} who wants to join or leave a group
* @param group the {@link Group} he wants to join or leave
@ -33,13 +30,15 @@ public final class GroupResize extends Event<User> {
*/
public GroupResize(User user, Group group, ElementOperation operation) {
super(user);
this.operation = operation;
this.operation = operation;
if (group.getContacts().contains(user)) {
if (operation.equals(ADD))
throw new IllegalArgumentException(String.format("Cannot add %s to %s!", user, group));
throw new IllegalArgumentException(String.format("Cannot add %s to %s!", user,
group));
} else if (operation.equals(REMOVE))
throw new IllegalArgumentException(String.format("Cannot remove %s from %s!", user, group));
groupID = group.getID();
throw new IllegalArgumentException(String.format("Cannot remove %s from %s!", user,
group));
groupID = group.getID();
}
/**
@ -72,5 +71,8 @@ public final class GroupResize extends Event<User> {
}
@Override
public String toString() { return String.format("GroupResize[userid=%d,groupid=%d,operation=%s]", get(), groupID, operation); }
public String toString() {
return String.format("GroupResize[userid=%d,groupid=%d,operation=%s]", get(), groupID,
operation);
}
}

View File

@ -1,8 +1,7 @@
package envoy.event;
/**
* Signifies to the client that the handshake failed for the attached
* reason.
* Signifies to the client that the handshake failed for the attached reason.
*
* @author Kai S. K. Engelbart
* @since Envoy Common v0.3-alpha
@ -24,8 +23,7 @@ public final class HandshakeRejection extends Event<String> {
public static final String USERNAME_TAKEN = "Incorrect user name or password.";
/**
* Select this value if the version of the client is incompatible with the
* server.
* Select this value if the version of the client is incompatible with the server.
*
* @since Envoy Common v0.1-beta
*/
@ -39,8 +37,7 @@ public final class HandshakeRejection extends Event<String> {
public static final String INVALID_TOKEN = "Invalid authentication token";
/**
* Select this value if the handshake could not be completed for some different
* reason.
* Select this value if the handshake could not be completed for some different reason.
*
* @since Envoy Common v0.3-alpha
*/
@ -54,7 +51,9 @@ public final class HandshakeRejection extends Event<String> {
*
* @since Envoy Common v0.3-alpha
*/
public HandshakeRejection() { super(INTERNAL_ERROR); }
public HandshakeRejection() {
super(INTERNAL_ERROR);
}
/**
* Creates an instance of {@link HandshakeRejection}.
@ -62,5 +61,7 @@ public final class HandshakeRejection extends Event<String> {
* @param reason the reason why the handshake was rejected
* @since Envoy Common v0.3-alpha
*/
public HandshakeRejection(String reason) { super(reason); }
public HandshakeRejection(String reason) {
super(reason);
}
}

View File

@ -1,8 +1,7 @@
package envoy.event;
/**
* Signifies to the server that the client needs a new
* {@link envoy.data.IDGenerator} instance.
* Signifies to the server that the client needs a new {@link envoy.data.IDGenerator} instance.
*
* @author Kai S. K. Engelbart
* @since Envoy Common v0.3-alpha

View File

@ -1,8 +1,7 @@
package envoy.event;
/**
* This event should be sent when a user is currently typing something in a
* chat.
* This event should be sent when a user is currently typing something in a chat.
*
* @author Leon Hofmeister
* @since Envoy Client v0.2-beta

View File

@ -1,8 +1,8 @@
package envoy.event;
/**
* This class allows envoy users to send an issue proposal to the server who, if
* not disabled by its administrator, will forward it directly to Gitea.
* This class allows envoy users to send an issue proposal to the server who, if not disabled by its
* administrator, will forward it directly to Gitea.
*
* @author Leon Hofmeister
* @since Envoy Common v0.2-beta
@ -17,9 +17,8 @@ public final class IssueProposal extends Event<String> {
/**
* @param title the title of the reported bug
* @param description the description of this bug
* @param isBug determines whether this {@code IssueProposal} is
* supposed to be a
* feature or a bug (true = bug, false = feature)
* @param isBug determines whether this {@code IssueProposal} is supposed to be a feature
* or a bug (true = bug, false = feature)
* @since Envoy Common v0.2-beta
*/
public IssueProposal(String title, String description, boolean isBug) {
@ -32,14 +31,14 @@ public final class IssueProposal extends Event<String> {
* @param title the title of the reported bug
* @param description the description of this bug
* @param user the name of the user creating the issue
* @param isBug determines whether this {@code IssueProposal} is
* supposed to be a
* feature or a bug (true = bug, false = feature)
* @param isBug determines whether this {@code IssueProposal} is supposed to be a feature
* or a bug (true = bug, false = feature)
* @since Envoy Common v0.2-beta
*/
public IssueProposal(String title, String description, String user, boolean isBug) {
super(escape(title));
this.description = sanitizeDescription(description) + String.format("<br>Submitted by user %s.", user);
this.description =
sanitizeDescription(description) + String.format("<br>Submitted by user %s.", user);
bug = isBug;
}
@ -61,7 +60,9 @@ public final class IssueProposal extends Event<String> {
* @return the escaped string
* @since Envoy Client v0.2-beta
*/
private static String escape(String raw) { return raw.replace("\\", "\\\\").replace("\"", "\\\""); }
private static String escape(String raw) {
return raw.replace("\\", "\\\\").replace("\"", "\\\"");
}
/**
* @return the description
@ -70,8 +71,7 @@ public final class IssueProposal extends Event<String> {
public String getDescription() { return description; }
/**
* @return whether this issue is supposed to be a bug - otherwise it is intended
* as a feature
* @return whether this issue is supposed to be a bug - otherwise it is intended as a feature
* @since Envoy Common v0.2-beta
*/
public boolean isBug() { return bug; }

View File

@ -19,8 +19,7 @@ public class MessageStatusChange extends Event<Message.MessageStatus> {
* Initializes a {@link MessageStatusChange}.
*
* @param id the ID of the {@link Message} this event is related to
* @param status the status of the {@link Message} this event is related
* to
* @param status the status of the {@link Message} this event is related to
* @param date the date at which the MessageStatus change occurred
* @since Envoy Common v0.2-beta
*/
@ -36,7 +35,9 @@ public class MessageStatusChange extends Event<Message.MessageStatus> {
* @param message the message from which to build the event
* @since Envoy Common v0.2-alpha
*/
public MessageStatusChange(Message message) { this(message.getID(), message.getStatus(), Instant.now()); }
public MessageStatusChange(Message message) {
this(message.getID(), message.getStatus(), Instant.now());
}
/**
* @return the ID of the {@link Message} this event is related to
@ -51,5 +52,7 @@ public class MessageStatusChange extends Event<Message.MessageStatus> {
public Instant getDate() { return date; }
@Override
public String toString() { return String.format("MessageStatusChange[id=%d,status=%s,date=%s]", id, value, date); }
public String toString() {
return String.format("MessageStatusChange[id=%d,status=%s,date=%s]", id, value, date);
}
}

View File

@ -5,8 +5,7 @@ import envoy.data.Contact;
/**
* This event informs
* <p>
* a) the server of the name change of a user or a group.
* b) another user of this users name change.
* a) the server of the name change of a user or a group. b) another user of this users name change.
*
* @author Leon Hofmeister
* @since Envoy Common v0.1-beta
@ -15,7 +14,7 @@ public final class NameChange extends Event<String> {
private final long id;
private static final long serialVersionUID = 0L;
private static final long serialVersionUID = 0L;
/**
* Creates a new {@link NameChange} for a user or a group.
@ -30,13 +29,14 @@ public final class NameChange extends Event<String> {
}
/**
* Initializes a {@link NameChange} through a Contact where the name has
* already been set.
* Initializes a {@link NameChange} through a Contact where the name has already been set.
*
* @param contact the contact whose name was updated
* @since Envoy Common v0.2-alpha
*/
public NameChange(Contact contact) { this(contact.getID(), contact.getName()); }
public NameChange(Contact contact) {
this(contact.getID(), contact.getName());
}
/**
* @return the ID of the {@link Contact} this event is related to
@ -45,5 +45,7 @@ public final class NameChange extends Event<String> {
public long getID() { return id; }
@Override
public String toString() { return String.format("NameChange[id=%d,name=%s]", id, value); }
public String toString() {
return String.format("NameChange[id=%d,name=%s]", id, value);
}
}

View File

@ -21,5 +21,7 @@ public class NewAuthToken extends Event<String> {
}
@Override
public String toString() { return "NewAuthToken"; }
public String toString() {
return "NewAuthToken";
}
}

View File

@ -1,8 +1,7 @@
package envoy.event;
/**
* This event is used so that the server can tell the client that attachments
* will be filtered out.
* This event is used so that the server can tell the client that attachments will be filtered out.
*
* @author Leon Hofmeister
* @since Envoy Common v0.2-beta

View File

@ -38,5 +38,7 @@ public final class PasswordChangeRequest extends Event<String> {
public String getOldPassword() { return oldPassword; }
@Override
public String toString() { return "PasswordChangeRequest[id=" + id + "]"; }
public String toString() {
return "PasswordChangeRequest[id=" + id + "]";
}
}

View File

@ -1,8 +1,8 @@
package envoy.event;
/**
* This class acts as a notice to the user whether his
* {@link envoy.event.PasswordChangeRequest} was successful.
* This class acts as a notice to the user whether his {@link envoy.event.PasswordChangeRequest} was
* successful.
*
* @author Leon Hofmeister
* @since Envoy Common v0.2-beta
@ -14,9 +14,10 @@ public final class PasswordChangeResult extends Event<Boolean> {
/**
* Creates an instance of {@code PasswordChangeResult}.
*
* @param value whether the preceding {@link envoy.event.PasswordChangeRequest}
* was successful.
* @param value whether the preceding {@link envoy.event.PasswordChangeRequest} was successful.
* @since Envoy Common v0.2-beta
*/
public PasswordChangeResult(boolean value) { super(value); }
public PasswordChangeResult(boolean value) {
super(value);
}
}

View File

@ -17,8 +17,7 @@ public final class UserStatusChange extends Event<UserStatus> {
* Initializes a {@link UserStatusChange}.
*
* @param id the ID of the {@link User} this event is related to
* @param status the status of the {@link User} this event is related
* to
* @param status the status of the {@link User} this event is related to
* @since Envoy Common v0.2-alpha
*/
public UserStatusChange(long id, User.UserStatus status) {
@ -32,7 +31,9 @@ public final class UserStatusChange extends Event<UserStatus> {
* @param user the User from which to build the event
* @since Envoy Common v0.2-alpha
*/
public UserStatusChange(User user) { this(user.getID(), user.getStatus()); }
public UserStatusChange(User user) {
this(user.getID(), user.getStatus());
}
/**
* @return the ID of the {@link User} this event is related to
@ -41,5 +42,7 @@ public final class UserStatusChange extends Event<UserStatus> {
public long getID() { return id; }
@Override
public String toString() { return String.format("UserStatusChange[id=%d,status=%s]", id, value); }
public String toString() {
return String.format("UserStatusChange[id=%d,status=%s]", id, value);
}
}

View File

@ -18,5 +18,7 @@ public final class UserSearchRequest extends Event<String> {
* @param searchPhrase the search phrase to use in the user search
* @since Envoy Common v0.2-alpha
*/
public UserSearchRequest(String searchPhrase) { super(searchPhrase); }
public UserSearchRequest(String searchPhrase) {
super(searchPhrase);
}
}

View File

@ -21,5 +21,7 @@ public final class UserSearchResult extends Event<List<User>> {
* @param users the users found during the search
* @since Envoy Common v0.2-alpha
*/
public UserSearchResult(List<User> users) { super(users); }
public UserSearchResult(List<User> users) {
super(users);
}
}

View File

@ -1,6 +1,6 @@
/**
* This package contains all events that can be sent or received by Envoy Client
* or Envoy Server Standalone.
* This package contains all events that can be sent or received by Envoy Client or Envoy Server
* Standalone.
*
* @author Leon Hofmeister
* @author Maximilian K&auml;fer

View File

@ -12,20 +12,24 @@ public final class EnvoyException extends Exception {
* @param message the message to display once this Exception is thrown
* @since Envoy Common v0.2-alpha
*/
public EnvoyException(String message) { super(message); }
public EnvoyException(String message) {
super(message);
}
/**
* @param message the message to display once this Exception is thrown
* @param cause the {@link Throwable} which resulted in the throw of an
* EnvoyException
* @param cause the {@link Throwable} which resulted in the throw of an EnvoyException
* @since Envoy Common v0.2-alpha
*/
public EnvoyException(String message, Throwable cause) { super(message, cause); }
public EnvoyException(String message, Throwable cause) {
super(message, cause);
}
/**
* @param cause the {@link Throwable} which resulted in the throw of an
* EnvoyException
* @param cause the {@link Throwable} which resulted in the throw of an EnvoyException
* @since Envoy Common v0.2-alpha
*/
public EnvoyException(Throwable cause) { super(cause); }
public EnvoyException(Throwable cause) {
super(cause);
}
}

View File

@ -26,13 +26,16 @@ public final class Bounds {
* @return {@code true} if the given contact name is valid
* @since Envoy Common v0.1-beta
*/
public static boolean isValidContactName(String contactName) { return CONTACT_NAME_PATTERN.matcher(contactName).matches(); }
public static boolean isValidContactName(String contactName) {
return CONTACT_NAME_PATTERN.matcher(contactName).matches();
}
/**
* @return the maximum size allowed for a user/ group name.
* @apiNote has to be updated manually if {@link Bounds#CONTACT_NAME_PATTERN}
* gets updated.
* @apiNote has to be updated manually if {@link Bounds#CONTACT_NAME_PATTERN} gets updated.
* @since Envoy Common v0.1-beta
*/
public static int maximumUsernameSize() { return 16; }
public static int maximumUsernameSize() {
return 16;
}
}

View File

@ -8,8 +8,7 @@ import java.util.logging.*;
import envoy.data.Config;
/**
* Configures the {@link java.util.logging} API to output the log into the
* console and a log file.
* Configures the {@link java.util.logging} API to output the log into the console and a log file.
*
* @author Leon Hofmeister
* @author Kai S. K. Engelbart
@ -30,20 +29,25 @@ public final class EnvoyLog {
* @since Envoy Common v0.1-beta
*/
public static void initialize(Config config) {
if (initialized) throw new IllegalStateException("EnvoyLog is already initialized");
if (initialized)
throw new IllegalStateException("EnvoyLog is already initialized");
// Remove default console handler
LogManager.getLogManager().reset();
// Configure log file
final File logFile = new File(config.getHomeDirectory(),
"log/" + DateTimeFormatter.ofPattern("yyyy-MM-dd--hh-mm").format(LocalDateTime.now()) + ".log");
"log/"
+ DateTimeFormatter.ofPattern("yyyy-MM-dd--hh-mm")
.format(LocalDateTime.now())
+ ".log");
logFile.getParentFile().mkdirs();
// Configure formatting
// Sample log entry: [2020-06-13 16:50:26] [INFORMATION]
// [envoy.client.ui.Startup] Closing connection...
System.setProperty("java.util.logging.SimpleFormatter.format", "[%1$tF %1$tT] [%4$-7s] [%3$s] %5$s %6$s%n");
System.setProperty("java.util.logging.SimpleFormatter.format",
"[%1$tF %1$tT] [%4$-7s] [%3$s] %5$s %6$s%n");
final SimpleFormatter formatter = new SimpleFormatter();
try {
@ -69,20 +73,22 @@ public final class EnvoyLog {
}
/**
* Configures all loggers that are contained within the hierarchy of a specific
* path to use the console and file handlers.
* Configures all loggers that are contained within the hierarchy of a specific path to use the
* console and file handlers.
*
* @param path the path to the loggers to configure
* @since Envoy Common v0.1-beta
*/
private static void attach(String path) {
if (!initialized) throw new IllegalStateException("EnvoyLog is not initialized");
if (!initialized)
throw new IllegalStateException("EnvoyLog is not initialized");
// Get root logger
final Logger logger = Logger.getLogger(path);
// Add handlers
if (fileHandler != null) logger.addHandler(fileHandler);
if (fileHandler != null)
logger.addHandler(fileHandler);
logger.addHandler(consoleHandler);
// Delegate logger level filtering to the handlers
@ -90,12 +96,14 @@ public final class EnvoyLog {
}
/**
* Creates a logger for a specified class, which output will be displayed inside
* the console and written to the log file.
* Creates a logger for a specified class, which output will be displayed inside the console and
* written to the log file.
*
* @param logClass the class in which the logger is used
* @return the created logger
* @since Envoy Common v0.1-beta
*/
public static Logger getLogger(Class<?> logClass) { return Logger.getLogger(logClass.getCanonicalName()); }
public static Logger getLogger(Class<?> logClass) {
return Logger.getLogger(logClass.getCanonicalName());
}
}

View File

@ -19,7 +19,9 @@ public final class SerializationUtils {
* @return a byte array of length 4
* @since Envoy Common v0.2-alpha
*/
public static byte[] intToBytes(int n) { return new byte[] { (byte) (n >>> 24), (byte) (n >>> 16), (byte) (n >>> 8), (byte) n }; }
public static byte[] intToBytes(int n) {
return new byte[] { (byte) (n >>> 24), (byte) (n >>> 16), (byte) (n >>> 8), (byte) n };
}
/**
* Converts four bytes in byte array to an integer
@ -30,8 +32,9 @@ public final class SerializationUtils {
* @since Envoy Common v0.2-alpha
*/
public static int bytesToInt(byte[] bytes, int offset) {
return (bytes[offset] & 0xFF) << 24 | (bytes[offset + 1] & 0xFF) << 16 | (bytes[offset + 2] & 0xFF) << 8
| (bytes[offset + 3] & 0xFF) << 0;
return (bytes[offset] & 0xFF) << 24 | (bytes[offset + 1] & 0xFF) << 16
| (bytes[offset + 2] & 0xFF) << 8
| (bytes[offset + 3] & 0xFF) << 0;
}
/**
@ -41,14 +44,15 @@ public final class SerializationUtils {
* @param file the file to deserialize from
* @param serializedClass the class of the object to deserialize
* @return the deserialized object
* @throws IOException if something failed while deserializing the
* object
* @throws ClassNotFoundException if the deserialized object can not be linked
* to a class
* @throws IOException if something failed while deserializing the object
* @throws ClassNotFoundException if the deserialized object can not be linked to a class
* @since Envoy Common v0.2-alpha
*/
public static <T extends Serializable> T read(File file, Class<T> serializedClass) throws IOException, ClassNotFoundException {
if (file == null) throw new NullPointerException("File is null");
public static <T extends Serializable> T read(File file,
Class<T> serializedClass) throws IOException,
ClassNotFoundException {
if (file == null)
throw new NullPointerException("File is null");
return read(new FileInputStream(file), serializedClass);
}
@ -59,13 +63,13 @@ public final class SerializationUtils {
* @param bytes the array in which the serialized object is stored
* @param serializedClass the class of the serialized object
* @return the deserialized object
* @throws IOException if something failed while deserializing the
* object
* @throws ClassNotFoundException if the deserialized object can not be linked
* to a class
* @throws IOException if something failed while deserializing the object
* @throws ClassNotFoundException if the deserialized object can not be linked to a class
* @since Envoy Common v0.2-alpha
*/
public static <T extends Serializable> T read(byte[] bytes, Class<T> serializedClass) throws IOException, ClassNotFoundException {
public static <T extends Serializable> T read(byte[] bytes,
Class<T> serializedClass) throws IOException,
ClassNotFoundException {
return read(new ByteArrayInputStream(bytes), serializedClass);
}
@ -74,16 +78,15 @@ public final class SerializationUtils {
*
* @param <T> the type of the serialized object
* @param in the {@link InputStream} of a serialized Object
* @param serializedClass the object type to convert the deserialized object
* into
* @param serializedClass the object type to convert the deserialized object into
* @return the deserialized object
* @throws IOException if something failed while deserializing the
* object
* @throws ClassNotFoundException if the deserialized object can not be linked
* to a class
* @throws IOException if something failed while deserializing the object
* @throws ClassNotFoundException if the deserialized object can not be linked to a class
* @since Envoy Common v0.2-alpha
*/
public static <T extends Serializable> T read(InputStream in, Class<T> serializedClass) throws IOException, ClassNotFoundException {
public static <T extends Serializable> T read(InputStream in,
Class<T> serializedClass) throws IOException,
ClassNotFoundException {
try (ObjectInputStream oin = new ObjectInputStream(in)) {
return serializedClass.cast(oin.readObject());
}
@ -98,14 +101,16 @@ public final class SerializationUtils {
* @since Envoy Common v0.2-alpha
*/
public static void write(File file, Object... objs) throws IOException {
if (file == null) throw new NullPointerException("File is null");
if (objs == null) throw new NullPointerException("Null array passed to serialize");
if (file == null)
throw new NullPointerException("File is null");
if (objs == null)
throw new NullPointerException("Null array passed to serialize");
if (!file.exists()) {
file.getParentFile().mkdirs();
file.createNewFile();
}
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file))) {
for (var obj : objs)
for (final var obj : objs)
out.writeObject(obj);
}
}
@ -119,7 +124,7 @@ public final class SerializationUtils {
* @since Envoy Common v0.2-alpha
*/
public static byte[] writeToByteArray(Object obj) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream oout = new ObjectOutputStream(baos)) {
oout.writeObject(obj);
}
@ -127,8 +132,8 @@ public final class SerializationUtils {
}
/**
* Serializes an object and writes it into an output stream preceded by 4 bytes
* containing the number of serialized bytes.
* Serializes an object and writes it into an output stream preceded by 4 bytes containing the
* number of serialized bytes.
*
* @param obj the object to serialize
* @param out the output stream to serialize to
@ -137,10 +142,10 @@ public final class SerializationUtils {
*/
public static void writeBytesWithLength(Object obj, OutputStream out) throws IOException {
// Serialize object to byte array
byte[] objBytes = writeToByteArray(obj);
final byte[] objBytes = writeToByteArray(obj);
// Get length of byte array in bytes
byte[] objLen = intToBytes(objBytes.length);
final byte[] objLen = intToBytes(objBytes.length);
// Write length and byte array
out.write(objLen);

View File

@ -1,7 +1,6 @@
/**
* This package contains general useful classes that can be used by both Envoy
* Client and Envoy Server Standalone and that could not be assigned to any
* other package.
* This package contains general useful classes that can be used by both Envoy Client and Envoy
* Server Standalone and that could not be assigned to any other package.
*
* @author Leon Hofmeister
* @author Maximilian K&auml;fer

View File

@ -1,6 +1,6 @@
/**
* This module contains all packages that are used by Envoy Client and Envoy
* Server Standalone at the same time.
* This module contains all packages that are used by Envoy Client and Envoy Server Standalone at
* the same time.
*
* @author Leon Hofmeister
* @author Maximilian K&auml;fer

Some files were not shown because too many files have changed in this diff Show More