From 3cc15ebd7eb8ade802b9a039873f6dbe5c185594 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 7 Apr 2013 21:52:49 +0200 Subject: [PATCH] Add basic IRC framework. --- .../java/net/pterodactylus/irc/Connection.java | 530 +++++++++++++++++++++ .../net/pterodactylus/irc/ConnectionBuilder.java | 107 +++++ src/main/java/net/pterodactylus/irc/Nickname.java | 82 ++++ .../net/pterodactylus/irc/NicknameChooser.java | 36 ++ .../irc/event/AbstractChannelEvent.java | 58 +++ .../irc/event/AbstractConnectionEvent.java | 55 +++ .../irc/event/AbstractReplyEvent.java | 61 +++ .../net/pterodactylus/irc/event/ChannelJoined.java | 58 +++ .../pterodactylus/irc/event/ChannelNicknames.java | 64 +++ .../pterodactylus/irc/event/ChannelNotJoined.java | 75 +++ .../net/pterodactylus/irc/event/ChannelTopic.java | 60 +++ .../irc/event/ConnectionEstablished.java | 40 ++ .../pterodactylus/irc/event/ConnectionFailed.java | 59 +++ .../net/pterodactylus/irc/event/MotdReceived.java | 58 +++ .../irc/event/NicknameInUseReceived.java | 72 +++ .../irc/event/NoNicknameGivenReceived.java | 42 ++ .../pterodactylus/irc/event/NoticeReceived.java | 72 +++ .../irc/event/UnknownReplyReceived.java | 43 ++ .../net/pterodactylus/irc/util/RandomNickname.java | 51 ++ 19 files changed, 1623 insertions(+) create mode 100644 src/main/java/net/pterodactylus/irc/Connection.java create mode 100644 src/main/java/net/pterodactylus/irc/ConnectionBuilder.java create mode 100644 src/main/java/net/pterodactylus/irc/Nickname.java create mode 100644 src/main/java/net/pterodactylus/irc/NicknameChooser.java create mode 100644 src/main/java/net/pterodactylus/irc/event/AbstractChannelEvent.java create mode 100644 src/main/java/net/pterodactylus/irc/event/AbstractConnectionEvent.java create mode 100644 src/main/java/net/pterodactylus/irc/event/AbstractReplyEvent.java create mode 100644 src/main/java/net/pterodactylus/irc/event/ChannelJoined.java create mode 100644 src/main/java/net/pterodactylus/irc/event/ChannelNicknames.java create mode 100644 src/main/java/net/pterodactylus/irc/event/ChannelNotJoined.java create mode 100644 src/main/java/net/pterodactylus/irc/event/ChannelTopic.java create mode 100644 src/main/java/net/pterodactylus/irc/event/ConnectionEstablished.java create mode 100644 src/main/java/net/pterodactylus/irc/event/ConnectionFailed.java create mode 100644 src/main/java/net/pterodactylus/irc/event/MotdReceived.java create mode 100644 src/main/java/net/pterodactylus/irc/event/NicknameInUseReceived.java create mode 100644 src/main/java/net/pterodactylus/irc/event/NoNicknameGivenReceived.java create mode 100644 src/main/java/net/pterodactylus/irc/event/NoticeReceived.java create mode 100644 src/main/java/net/pterodactylus/irc/event/UnknownReplyReceived.java create mode 100644 src/main/java/net/pterodactylus/irc/util/RandomNickname.java diff --git a/src/main/java/net/pterodactylus/irc/Connection.java b/src/main/java/net/pterodactylus/irc/Connection.java new file mode 100644 index 0000000..158a7f1 --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/Connection.java @@ -0,0 +1,530 @@ +/* + * XdccDownloader - Connection.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc; + +import static com.google.common.base.Preconditions.checkState; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.Socket; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.SynchronousQueue; +import javax.net.SocketFactory; + +import net.pterodactylus.irc.event.ChannelJoined; +import net.pterodactylus.irc.event.ChannelNicknames; +import net.pterodactylus.irc.event.ChannelNotJoined; +import net.pterodactylus.irc.event.ChannelNotJoined.Reason; +import net.pterodactylus.irc.event.ChannelTopic; +import net.pterodactylus.irc.event.ConnectionEstablished; +import net.pterodactylus.irc.event.ConnectionFailed; +import net.pterodactylus.irc.event.MotdReceived; +import net.pterodactylus.irc.event.NicknameInUseReceived; +import net.pterodactylus.irc.event.NoNicknameGivenReceived; +import net.pterodactylus.irc.event.UnknownReplyReceived; +import net.pterodactylus.irc.util.RandomNickname; + +import com.beust.jcommander.internal.Maps; +import com.beust.jcommander.internal.Sets; +import com.google.common.base.Optional; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; +import com.google.common.io.Closeables; +import com.google.common.util.concurrent.AbstractExecutionThreadService; +import com.google.common.util.concurrent.Service; + +/** + * A connection to an IRC server. + * + * @author David ‘Bombe’ Roden + */ +public class Connection extends AbstractExecutionThreadService implements Service { + + /** The event bus. */ + private final EventBus eventBus; + + /** The socket factory. */ + private final SocketFactory socketFactory; + + /** The hostname to connect to. */ + private final String hostname; + + /** The port to connect to. */ + private final int port; + + /** The nickname chooser. */ + private NicknameChooser nicknameChooser = new NicknameChooser() { + + @Override + public String getNickname() { + return RandomNickname.get(); + } + }; + + /** The nickname. */ + private String nickname = null; + + /** The username. */ + private Optional username = Optional.absent(); + + /** The real name. */ + private Optional realName = Optional.absent(); + + /** The optional password for the connection. */ + private Optional password = Optional.absent(); + + /** The connection handler. */ + private ConnectionHandler connectionHandler; + + /** + * Creates a new connection. + * + * @param eventBus + * The event bus + * @param socketFactory + * The socket factory + * @param hostname + * The hostname of the IRC server + * @param port + * The port number of the IRC server + */ + public Connection(EventBus eventBus, SocketFactory socketFactory, String hostname, int port) { + this.eventBus = eventBus; + this.socketFactory = socketFactory; + this.hostname = hostname; + this.port = port; + } + + // + // ACCESSORS + // + + /** + * Returns the nickname that is currently in use by this connection. The + * nickname is only available once the connection has been {@link #start()}ed. + * + * @return The current nickname + */ + public String nickname() { + return nickname; + } + + // + // MUTATORS + // + + /** + * Sets the nickname chooser. The nickname chooser is only used during the + * creation of the connection. + * + * @param nicknameChooser + * The nickname chooser + * @return This connection + */ + public Connection nicknameChooser(NicknameChooser nicknameChooser) { + this.nicknameChooser = nicknameChooser; + return this; + } + + /** + * Sets the username to use. + * + * @param username + * The username to use + * @return + */ + public Connection username(String username) { + this.username = Optional.fromNullable(username); + return this; + } + + /** + * Sets the real name to use. + * + * @param realName + * The real name to use + * @return This connection + */ + public Connection realName(String realName) { + this.realName = Optional.fromNullable(realName); + return this; + } + + /** + * Sets the optional password for the connection. + * + * @param password + * The password for the connection + * @return This connection + */ + public Connection password(String password) { + this.password = Optional.fromNullable(password); + return this; + } + + // + // ACTIONS + // + + /** + * Joins the given channel. + * + * @param channel + * The channel to join + * @return {@code true} if the channel was joined, {@code false} otherwise + */ + public boolean joinChannel(final String channel) { + final SynchronousQueue result = new SynchronousQueue(); + Object eventHandler = new Object() { + + @Subscribe + public void channelJoined(ChannelJoined channelJoined) throws InterruptedException { + if (!channelJoined.channel().equalsIgnoreCase(channel)) { + return; + } + Optional nickname = channelJoined.client().nick(); + if (!nickname.isPresent() || (nickname.isPresent() && nickname().equalsIgnoreCase(nickname.get()))) { + eventBus.unregister(this); + result.put(true); + } + } + + @Subscribe + public void channelNotJoined(ChannelNotJoined channelNotJoined) throws InterruptedException { + if (!channelNotJoined.channel().equalsIgnoreCase(channel)) { + return; + } + eventBus.unregister(this); + result.put(false); + } + }; + eventBus.register(eventHandler); + try { + connectionHandler.sendCommand("JOIN", channel); + return result.take(); + } catch (IOException ioe1) { + eventBus.unregister(eventHandler); + } catch (InterruptedException ie1) { + /* TODO - how to handle? */ + } + return false; + } + + // + // ABSTRACTEXECUTIONTHREADSERVICE METHODS + // + + @Override + protected void startUp() throws IllegalStateException { + checkState(username.isPresent(), "username must be set"); + checkState(realName.isPresent(), "realName must be set"); + } + + @Override + protected void run() { + + /* connect to remote socket. */ + try { + Socket socket = socketFactory.createSocket(hostname, port); + connectionHandler = new ConnectionHandler(socket.getInputStream(), socket.getOutputStream()); + + /* register connection. */ + if (password.isPresent()) { + connectionHandler.sendCommand("PASSWORD", password.get()); + } + connectionHandler.sendCommand("USER", username.get(), "8", "*", realName.get()); + nickname = nicknameChooser.getNickname(); + connectionHandler.sendCommand("NICK", nickname); + + } catch (IOException ioe1) { + eventBus.post(new ConnectionFailed(this, ioe1)); + return; + } + + /* now read replies and react. */ + try { + /* some status variables. */ + int oldConnectionStatus = 0; + int connectionStatus = 0; + boolean connected = true; + StringBuilder motd = new StringBuilder(); + Set nicks = Sets.newHashSet(); + + /* server modes. */ + Map nickPrefixes = Maps.newHashMap(); + + while (connected) { + Reply reply = connectionHandler.readReply(); + System.err.println("<< " + reply); + String command = reply.command(); + List parameters = reply.parameters(); + + /* replies 001-004 don’t hold information but they have to be sent on a successful connection. */ + if (command.equals("001")) { + connectionStatus |= 0x01; + } else if (command.equals("002")) { + connectionStatus |= 0x02; + } else if (command.equals("003")) { + connectionStatus |= 0x04; + } else if (command.equals("004")) { + connectionStatus |= 0x08; + + /* 005 originally was a bounce message, now used to transmit useful information about the server. */ + } else if (command.equals("005")) { + for (String parameter : parameters) { + if (parameter.startsWith("PREFIX=")) { + int openParen = parameter.indexOf('('); + int closeParen = parameter.indexOf(')'); + if ((openParen != -1) && (closeParen != -1)) { + for (int modeCharacterIndex = 1; modeCharacterIndex < (closeParen - openParen); ++modeCharacterIndex) { + char modeCharacter = parameter.charAt(openParen + modeCharacterIndex); + char modeSymbol = parameter.charAt(closeParen + modeCharacterIndex); + nickPrefixes.put(String.valueOf(modeSymbol), String.valueOf(modeCharacter)); + } + } + } + } + + /* 375, 372, and 376 handle the server’s MOTD. */ + } else if (command.equals("375")) { + /* MOTD starts. */ + motd.append(parameters.get(1)).append('\n'); + } else if (command.equals("372")) { + motd.append(parameters.get(1)).append('\n'); + } else if (command.equals("376")) { + motd.append(parameters.get(1)).append('\n'); + eventBus.post(new MotdReceived(this, motd.toString())); + motd.setLength(0); + + /* 43x replies are for nick change errors. */ + } else if (command.equals("431")) { + eventBus.post(new NoNicknameGivenReceived(this, reply)); + } else if (command.equals("433")) { + if (connectionStatus == 0) { + nickname = nicknameChooser.getNickname(); + connectionHandler.sendCommand("NICK", nickname); + } else { + eventBus.post(new NicknameInUseReceived(this, reply)); + } + + /* channel stuff. */ + } else if (command.equalsIgnoreCase("JOIN")) { + eventBus.post(new ChannelJoined(this, parameters.get(0), reply.source().get())); + } else if (command.equals("331")) { + /* no topic is set. */ + } else if (command.equals("332")) { + eventBus.post(new ChannelTopic(this, parameters.get(1), parameters.get(2))); + } else if (command.equals("353")) { + String channel = parameters.get(2); + for (String nickname : parameters.get(3).split(" ")) { + if (nickPrefixes.containsKey(nickname.substring(0, 1))) { + nicks.add(new Nickname(nickname.substring(1), nickname.substring(0, 1))); + } else { + nicks.add(new Nickname(nickname, "")); + } + } + } else if (command.equals("366")) { + eventBus.post(new ChannelNicknames(this, parameters.get(1), nicks)); + System.out.println("Found Nicknames: " + nicks); + nicks.clear(); + + /* common channel join errors. */ + } else if (command.equals("474")) { + eventBus.post(new ChannelNotJoined(this, parameters.get(1), Reason.banned)); + } else if (command.equals("473")) { + eventBus.post(new ChannelNotJoined(this, parameters.get(1), Reason.inviteOnly)); + } else if (command.equals("475")) { + eventBus.post(new ChannelNotJoined(this, parameters.get(1), Reason.badChannelKey)); + + /* basic connection housekeeping. */ + } else if (command.equalsIgnoreCase("PING")) { + connectionHandler.sendCommand("PONG", getOptional(parameters, 0), getOptional(parameters, 1)); + + /* okay, everything else. */ + } else { + eventBus.post(new UnknownReplyReceived(this, reply)); + } + + if ((connectionStatus == 0x0f) && (connectionStatus != oldConnectionStatus)) { + /* connection succeeded! */ + eventBus.post(new ConnectionEstablished(this)); + } + oldConnectionStatus = connectionStatus; + } + } catch (IOException ioe1) { + ioe1.printStackTrace(); + } finally { + System.out.println("Closing Connection."); + try { + Closeables.close(connectionHandler, true); + } catch (IOException ioe1) { + /* will not be thrown. */ + } + } + + } + + // + // PRIVATE METHODS + // + + /** + * Returns an item from the list, or {@link Optional#absent()} if the list is + * shorter than required for the given index. + * + * @param list + * The list to get an item from + * @param index + * The index of the item + * @param + * The type of the list items + * @return This list item wrapped in an {@link Optional}, or {@link + * Optional#absent()} if the list is not long enough + */ + private static Optional getOptional(List list, int index) { + if (index < list.size()) { + return Optional.fromNullable(list.get(index)); + } + return Optional.absent(); + } + + /** Handles input and output for the connection. */ + private class ConnectionHandler implements Closeable { + + /** The output stream of the connection. */ + private final OutputStream outputStream; + + /** The input stream of the connection. */ + private final BufferedReader inputStreamReader; + + /** + * Creates a new connection handler for the given input stream and output + * stream. + * + * @param inputStream + * The input stream of the connection + * @param outputStream + * The output stream of the connection + * @throws UnsupportedEncodingException + * if the encoding (currently “UTF-8”) is not valid + */ + private ConnectionHandler(InputStream inputStream, OutputStream outputStream) throws UnsupportedEncodingException { + this.outputStream = outputStream; + inputStreamReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + } + + // + // ACTIONS + // + + /** + * Sends a command with the given parameters, skipping all {@link + * Optional#absent()} optionals. + * + * @param command + * The command to send + * @param parameters + * The parameters + * @throws IOException + * if an I/O error occurs + */ + public void sendCommand(String command, Optional... parameters) throws IOException { + List setParameters = new ArrayList(); + for (Optional maybeSetParameter : parameters) { + if (maybeSetParameter.isPresent()) { + setParameters.add(maybeSetParameter.get()); + } + } + sendCommand(command, setParameters.toArray(new String[0])); + } + + /** + * Sends a command with the given parameters. + * + * @param command + * The command to send + * @param parameters + * The parameters of the command + * @throws IOException + * if an I/O error occurs + * @throws IllegalArgumentException + * if any parameter but that last contains a space character + */ + public void sendCommand(String command, String... parameters) throws IOException, IllegalArgumentException { + StringBuilder commandBuilder = new StringBuilder(); + + commandBuilder.append(command); + for (int parameterIndex = 0; parameterIndex < parameters.length; ++parameterIndex) { + String parameter = parameters[parameterIndex]; + /* space is only allowed in the last parameter. */ + commandBuilder.append(' '); + if (parameter.contains(" ")) { + if (parameterIndex == (parameters.length - 1)) { + commandBuilder.append(':'); + } else { + throw new IllegalArgumentException(String.format("parameter “%s” must not contain space!", parameter)); + } + } + commandBuilder.append(parameter); + } + + System.out.println(">> " + commandBuilder.toString()); + outputStream.write((commandBuilder.toString() + "\r\n").getBytes("UTF-8")); + outputStream.flush(); + } + + /** + * Reads a line of reply from the connection. + * + * @return The reply + * @throws IOException + * if an I/O error occurs + * @throws EOFException + * if EOF was reached + */ + public Reply readReply() throws IOException, EOFException { + String line = inputStreamReader.readLine(); + if (line == null) { + throw new EOFException(); + } + + return Reply.parseLine(line); + } + + // + // CLOSEABLE METHODS + // + + @Override + public void close() throws IOException { + Closeables.close(outputStream, true); + Closeables.close(inputStreamReader, true); + } + + } + +} diff --git a/src/main/java/net/pterodactylus/irc/ConnectionBuilder.java b/src/main/java/net/pterodactylus/irc/ConnectionBuilder.java new file mode 100644 index 0000000..feefe1f --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/ConnectionBuilder.java @@ -0,0 +1,107 @@ +/* + * XdccDownloader - ConnectionBuilder.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; + +import com.google.common.base.Optional; +import com.google.common.eventbus.EventBus; +import com.google.inject.Inject; + +/** + * Builder for {@link Connection}s. + * + * @author David ‘Bombe’ Roden + */ +public class ConnectionBuilder { + + /** The event bus. */ + private final EventBus eventBus; + + /** The hostname to connect to. */ + private Optional hostname = Optional.absent(); + + /** The port number to connect to. */ + private int port = 6666; + + /** Whether to use an SSL connection. */ + private boolean secure = false; + + /** + * Creates a new connection builder. + * + * @param eventBus + * The event bus + */ + @Inject + public ConnectionBuilder(EventBus eventBus) { + this.eventBus = eventBus; + } + + /** + * Configures this builder to use the given hostname for the connection. + * + * @param hostname + * The hostname of the IRC server + * @return This builder + */ + public ConnectionBuilder connect(String hostname) { + this.hostname = Optional.fromNullable(hostname); + return this; + } + + /** + * Configures this builder to use the given port number for the connection. + * + * @param port + * The port number of the IRC server + * @return This builder + */ + public ConnectionBuilder port(int port) { + this.port = port; + return this; + } + + /** + * Configures this builder to use an SSL connection. + * + * @return This builder + */ + public ConnectionBuilder secure() { + this.secure = true; + return this; + } + + /** + * Builds the connection. + * + * @return The created connection + * @throws IllegalStateException + * if the hostname has not been set + */ + public Connection build() throws IllegalStateException { + if (!hostname.isPresent()) { + throw new IllegalStateException("hostname must not be empty"); + } + + SocketFactory socketFactory = secure ? SSLSocketFactory.getDefault() : SocketFactory.getDefault(); + return new Connection(eventBus, socketFactory, hostname.get(), port); + } + +} diff --git a/src/main/java/net/pterodactylus/irc/Nickname.java b/src/main/java/net/pterodactylus/irc/Nickname.java new file mode 100644 index 0000000..25f2497 --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/Nickname.java @@ -0,0 +1,82 @@ +/* + * XdccDownloader - Nick.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc; + +/** + * Container for a nickname and a prefix. The prefix is a character specific to + * an IRC server that denotes some kind of special status of a nickname in a + * channel. The most commonly known prefixes are “@” (for operators) and “+” + * (for voiced clients) but newer IRCds know other prefixes, such as “!” and + * “%”, too. + * + * @author David ‘Bombe’ Roden + */ +public class Nickname { + + /** The name. */ + private final String name; + + /** The prefix. */ + private final String prefix; + + /** + * Creates a new nickname. + * + * @param name + * The name of the nickname + * @param prefix + * The prefix of the nickname + */ + public Nickname(String name, String prefix) { + this.name = name; + this.prefix = prefix; + } + + // + // ACCESSORS + // + + /** + * Returns the name of the nickname. + * + * @return The name of the nickname + */ + public String name() { + return name; + } + + /** + * Returns the prefix of the nickname. + * + * @return The prefix of the nickname (or an empty string if this nickname does + * not have a prefix) + */ + public String prefix() { + return prefix; + } + + // + // OBJECT METHODS + // + + @Override + public String toString() { + return prefix + name; + } + +} diff --git a/src/main/java/net/pterodactylus/irc/NicknameChooser.java b/src/main/java/net/pterodactylus/irc/NicknameChooser.java new file mode 100644 index 0000000..9a9357a --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/NicknameChooser.java @@ -0,0 +1,36 @@ +/* + * XdccDownloader - NicknameChooser.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc; + +/** + * A nickname choose is used to get a new nickname. It is used by the {@link + * Connection} to retrieve the initial nickname of the connection, and it’s also + * used in case a nick change results in a “nickname already in use” error. + * + * @author David ‘Bombe’ Roden + */ +public interface NicknameChooser { + + /** + * Returns a nickname to use. + * + * @return A nickname + */ + public String getNickname(); + +} diff --git a/src/main/java/net/pterodactylus/irc/event/AbstractChannelEvent.java b/src/main/java/net/pterodactylus/irc/event/AbstractChannelEvent.java new file mode 100644 index 0000000..960e8f6 --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/event/AbstractChannelEvent.java @@ -0,0 +1,58 @@ +/* + * XdccDownloader - ChannelJoined.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.event; + +import net.pterodactylus.irc.Connection; + +/** + * Abstract base class for all events that happen on a channel. + * + * @author David ‘Bombe’ Roden + */ +public abstract class AbstractChannelEvent extends AbstractConnectionEvent { + + /** The channel this event occured on. */ + private final String channel; + + /** + * Creates a new abstract channel event. + * + * @param connection + * The connection this event occured on + * @param channel + * The channel this event occured on + */ + protected AbstractChannelEvent(Connection connection, String channel) { + super(connection); + this.channel = channel; + } + + // + // ACCESSORS + // + + /** + * Returns the channel this event occured on. + * + * @return The channel this event occured on + */ + public String channel() { + return channel; + } + +} diff --git a/src/main/java/net/pterodactylus/irc/event/AbstractConnectionEvent.java b/src/main/java/net/pterodactylus/irc/event/AbstractConnectionEvent.java new file mode 100644 index 0000000..f862487 --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/event/AbstractConnectionEvent.java @@ -0,0 +1,55 @@ +/* + * XdccDownloader - AbstractConnectionEvent.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.event; + +import net.pterodactylus.irc.Connection; + +/** + * Abstract base class for all {@link Connection}-related events. + * + * @author David ‘Bombe’ Roden + */ +public abstract class AbstractConnectionEvent { + + /** The connection the event occured on. */ + private final Connection connection; + + /** + * Creates a new abstract connection event. + * + * @param connection + * The connection the event occured on + */ + protected AbstractConnectionEvent(Connection connection) { + this.connection = connection; + } + + // + // ACCESSORS + // + + /** + * Returns the connection the event occured on. + * + * @return The connection the event occured on + */ + public Connection connection() { + return connection; + } + +} diff --git a/src/main/java/net/pterodactylus/irc/event/AbstractReplyEvent.java b/src/main/java/net/pterodactylus/irc/event/AbstractReplyEvent.java new file mode 100644 index 0000000..e940c6c --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/event/AbstractReplyEvent.java @@ -0,0 +1,61 @@ +/* + * XdccDownloader - AbstractReplyEvent.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.event; + +import net.pterodactylus.irc.Connection; +import net.pterodactylus.irc.Reply; + +/** + * Abstract base class for all events based on {@link Reply}s (and thus very low + * level). Examples would be e.g. nick changing messages, mode changes, private + * messages, and other single/global events that do not carry state. + * + * @author David ‘Bombe’ Roden + */ +public abstract class AbstractReplyEvent extends AbstractConnectionEvent { + + /** The reply that caused this event. */ + private final Reply reply; + + /** + * Creates a new abstract reply event. + * + * @param connection + * The connection the event occured on + * @param reply + * The reply that caused this event + */ + protected AbstractReplyEvent(Connection connection, Reply reply) { + super(connection); + this.reply = reply; + } + + // + // ACCESSORS + // + + /** + * Returns the reply that caused this event. + * + * @return The reply that caused this event + */ + public Reply reply() { + return reply; + } + +} diff --git a/src/main/java/net/pterodactylus/irc/event/ChannelJoined.java b/src/main/java/net/pterodactylus/irc/event/ChannelJoined.java new file mode 100644 index 0000000..d607ab1 --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/event/ChannelJoined.java @@ -0,0 +1,58 @@ +/* + * XdccDownloader - ChannelJoined.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.event; + +import net.pterodactylus.irc.Connection; +import net.pterodactylus.irc.Source; + +/** + * Event that notifies a listener that a client has joined a channel. + * + * @author David ‘Bombe’ Roden + */ +public class ChannelJoined extends AbstractChannelEvent { + + private final Source client; + + /** + * Creates a new channel joined event. + * + * @param connection + * The connection that joined the channel + * @param channel + * The channel that was joined + */ + public ChannelJoined(Connection connection, String channel, Source client) { + super(connection, channel); + this.client = client; + } + + // + // ACCESSORS + // + + /** + * Returns the client that joined the channel. + * + * @return The client that joined the channel + */ + public Source client() { + return client; + } + +} diff --git a/src/main/java/net/pterodactylus/irc/event/ChannelNicknames.java b/src/main/java/net/pterodactylus/irc/event/ChannelNicknames.java new file mode 100644 index 0000000..5ceaf09 --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/event/ChannelNicknames.java @@ -0,0 +1,64 @@ +/* + * XdccDownloader - ChannelNames.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.event; + +import java.util.Collection; + +import net.pterodactylus.irc.Connection; +import net.pterodactylus.irc.Nickname; + +/** + * Notifies a listener that a list of nicknames has been retrieved for a + * channel. + * + * @author David ‘Bombe’ Roden + */ +public class ChannelNicknames extends AbstractChannelEvent { + + /** The nicknames. */ + private final Collection nicknames; + + /** + * Creates a new channel nicknames event. + * + * @param connection + * The connection the event occured on + * @param channel + * The channel the nicknames are listed for + * @param nicknames + * The nicknames + */ + public ChannelNicknames(Connection connection, String channel, Collection nicknames) { + super(connection, channel); + this.nicknames = nicknames; + } + + // + // ACCESSORS + // + + /** + * Returns the nicknames retrieved for the channel. + * + * @return The nicknames retrieved for the channel + */ + public Collection nicknames() { + return nicknames; + } + +} diff --git a/src/main/java/net/pterodactylus/irc/event/ChannelNotJoined.java b/src/main/java/net/pterodactylus/irc/event/ChannelNotJoined.java new file mode 100644 index 0000000..daaeded --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/event/ChannelNotJoined.java @@ -0,0 +1,75 @@ +/* + * XdccDownloader - ChannelNotJoined.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.event; + +import net.pterodactylus.irc.Connection; + +/** + * Notifies a listener that a channel could not be joined. + * + * @author David ‘Bombe’ Roden + */ +public class ChannelNotJoined extends AbstractChannelEvent { + + /** The possible reasons for the failure to join a channel. */ + public enum Reason { + + channelIsFull, + inviteOnly, + banned, + badChannelKey, + badChannelMask, + noSuchChannel, + tooManyChannels, + tooManyTargets, + unavailable + + } + + /** The reason for the join failure. */ + private final Reason reason; + + /** + * Creates a new channel not joined event. + * + * @param connection + * The connection this event occured on + * @param channel + * The channel that could not be joined + * @param reason + * The reason for the failure to join the channel + */ + public ChannelNotJoined(Connection connection, String channel, Reason reason) { + super(connection, channel); + this.reason = reason; + } + + // + // ACCESSORS + // + + /** + * Returns the reason for the failure to join the channel. + * + * @return The reason for the failure to join the channel + */ + public Reason reason() { + return reason; + } + +} diff --git a/src/main/java/net/pterodactylus/irc/event/ChannelTopic.java b/src/main/java/net/pterodactylus/irc/event/ChannelTopic.java new file mode 100644 index 0000000..6f22461 --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/event/ChannelTopic.java @@ -0,0 +1,60 @@ +/* + * XdccDownloader - ChannelTopic.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.event; + +import net.pterodactylus.irc.Connection; + +/** + * Notifies a listener that a channel topic has been set. + * + * @author David ‘Bombe’ Roden + */ +public class ChannelTopic extends AbstractChannelEvent { + + /** The topic that has been set. */ + private final String topic; + + /** + * Creates a new channel topic event. + * + * @param connection + * The connection the event occured on + * @param channel + * The channel the topic was set on + * @param topic + * The topic that was set + */ + public ChannelTopic(Connection connection, String channel, String topic) { + super(connection, channel); + this.topic = topic; + } + + // + // ACCESSORS + // + + /** + * Returns the topic that was set. + * + * @return The topic that was set + */ + public String topic() { + return topic; + } + +} diff --git a/src/main/java/net/pterodactylus/irc/event/ConnectionEstablished.java b/src/main/java/net/pterodactylus/irc/event/ConnectionEstablished.java new file mode 100644 index 0000000..39aad14 --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/event/ConnectionEstablished.java @@ -0,0 +1,40 @@ +/* + * XdccDownloader - ConnectionEstablished.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.event; + +import net.pterodactylus.irc.Connection; + +/** + * Notifies a listener that a connection to an IRC server was established + * successfully. + * + * @author David ‘Bombe’ Roden + */ +public class ConnectionEstablished extends AbstractConnectionEvent { + + /** + * Creates a new connection established event. + * + * @param connection + * The connection the event occured on + */ + public ConnectionEstablished(Connection connection) { + super(connection); + } + +} diff --git a/src/main/java/net/pterodactylus/irc/event/ConnectionFailed.java b/src/main/java/net/pterodactylus/irc/event/ConnectionFailed.java new file mode 100644 index 0000000..9bd57ce --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/event/ConnectionFailed.java @@ -0,0 +1,59 @@ +/* + * XdccDownloader - ConnectionFailed.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.event; + +import net.pterodactylus.irc.Connection; + +/** + * Notifies a listener that a connection to an IRC server could not be + * established. + * + * @author David ‘Bombe’ Roden + */ +public class ConnectionFailed extends AbstractConnectionEvent { + + /** The cause of the connection failure. */ + private final Throwable cause; + + /** + * Creates a new connection failure event. + * + * @param connection + * The connection the event occured on + * @param cause + * The cause of the connection failure + */ + public ConnectionFailed(Connection connection, Throwable cause) { + super(connection); + this.cause = cause; + } + + // + // ACCESSORS + // + + /** + * Returns the cause of the connection failure. + * + * @return The cause of the connection failure + */ + public Throwable cause() { + return cause; + } + +} diff --git a/src/main/java/net/pterodactylus/irc/event/MotdReceived.java b/src/main/java/net/pterodactylus/irc/event/MotdReceived.java new file mode 100644 index 0000000..1e0a637 --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/event/MotdReceived.java @@ -0,0 +1,58 @@ +/* + * XdccDownloader - MotdReceived.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.event; + +import net.pterodactylus.irc.Connection; + +/** + * Notifies a listener that a server’s MOTD (message of the day) was received. + * + * @author David ‘Bombe’ Roden + */ +public class MotdReceived extends AbstractConnectionEvent { + + /** The MOTD of the server. */ + private final String motd; + + /** + * Creates a new MOTD received event. + * + * @param connection + * The connection the event occured on + * @param motd + * The MOTD of the server + */ + public MotdReceived(Connection connection, String motd) { + super(connection); + this.motd = motd; + } + + // + // ACCESSORS + // + + /** + * Returns the MOTD of the server. + * + * @return The MOTD of the server + */ + public String motd() { + return motd; + } + +} diff --git a/src/main/java/net/pterodactylus/irc/event/NicknameInUseReceived.java b/src/main/java/net/pterodactylus/irc/event/NicknameInUseReceived.java new file mode 100644 index 0000000..3a76914 --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/event/NicknameInUseReceived.java @@ -0,0 +1,72 @@ +/* + * XdccDownloader - NicknameInUseReceived.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.event; + +import net.pterodactylus.irc.Connection; +import net.pterodactylus.irc.Reply; + +/** + * Notifies a listener that a nickname is already in use. + * + * @author David ‘Bombe’ Roden + */ +public class NicknameInUseReceived extends AbstractReplyEvent { + + /** The nickname that is already in use. */ + private final String nickname; + + /** The message from the server. */ + private final String message; + + /** + * Creates a new nickname in use event. + * + * @param connection + * The connection the event occured on + * @param reply + * The reply that caused the event + */ + public NicknameInUseReceived(Connection connection, Reply reply) { + super(connection, reply); + nickname = reply.parameters().get(0); + message = reply.parameters().get(1); + } + + // + // ACCESSORS + // + + /** + * Returns the nickname that is already in use. + * + * @return The nickname that is already in use + */ + public String nickname() { + return nickname; + } + + /** + * Returns the message from the IRC server. + * + * @return The message from the IRC server + */ + public String message() { + return message; + } + +} diff --git a/src/main/java/net/pterodactylus/irc/event/NoNicknameGivenReceived.java b/src/main/java/net/pterodactylus/irc/event/NoNicknameGivenReceived.java new file mode 100644 index 0000000..aba2fb2 --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/event/NoNicknameGivenReceived.java @@ -0,0 +1,42 @@ +/* + * XdccDownloader - NoNicknameGivenReceived.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.event; + +import net.pterodactylus.irc.Connection; +import net.pterodactylus.irc.Reply; + +/** + * Notifies a listener that no nickname was given with a “NICK” command. + * + * @author David ‘Bombe’ Roden + */ +public class NoNicknameGivenReceived extends AbstractReplyEvent { + + /** + * Creates a new no nickname given event. + * + * @param connection + * The connection the event occured on + * @param reply + * The reply that caused the event + */ + public NoNicknameGivenReceived(Connection connection, Reply reply) { + super(connection, reply); + } + +} diff --git a/src/main/java/net/pterodactylus/irc/event/NoticeReceived.java b/src/main/java/net/pterodactylus/irc/event/NoticeReceived.java new file mode 100644 index 0000000..f4b34c8 --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/event/NoticeReceived.java @@ -0,0 +1,72 @@ +/* + * XdccDownloader - NoticeReceived.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.event; + +import net.pterodactylus.irc.Connection; +import net.pterodactylus.irc.Reply; + +/** + * Notifies a listener that a notice was received. + * + * @author David ‘Bombe’ Roden + */ +public class NoticeReceived extends AbstractReplyEvent { + + /** The target of the notice. */ + private final String target; + + /** The text of the notice. */ + private final String text; + + /** + * Creates a new notice received event. + * + * @param connection + * The connection the event occured on + * @param reply + * The reply that caused the event + */ + public NoticeReceived(Connection connection, Reply reply) { + super(connection, reply); + this.target = reply.parameters().get(0); + this.text = reply.parameters().get(1); + } + + // + // ACCESSORS + // + + /** + * Returns the target of the notice. + * + * @return The target of the notice + */ + public String target() { + return target; + } + + /** + * Returns the text of the notice. + * + * @return The text of the notice + */ + public String text() { + return text; + } + +} diff --git a/src/main/java/net/pterodactylus/irc/event/UnknownReplyReceived.java b/src/main/java/net/pterodactylus/irc/event/UnknownReplyReceived.java new file mode 100644 index 0000000..040be97 --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/event/UnknownReplyReceived.java @@ -0,0 +1,43 @@ +/* + * XdccDownloader - UnknownReplyReceived.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.event; + +import net.pterodactylus.irc.Connection; +import net.pterodactylus.irc.Reply; + +/** + * Notifies a listener that a reply was received that was not handled + * internally. + * + * @author David ‘Bombe’ Roden + */ +public class UnknownReplyReceived extends AbstractReplyEvent { + + /** + * Creates a new unknown reply event. + * + * @param connection + * The connection the event occured on + * @param reply + * The reply that caused the event + */ + public UnknownReplyReceived(Connection connection, Reply reply) { + super(connection, reply); + } + +} diff --git a/src/main/java/net/pterodactylus/irc/util/RandomNickname.java b/src/main/java/net/pterodactylus/irc/util/RandomNickname.java new file mode 100644 index 0000000..d678134 --- /dev/null +++ b/src/main/java/net/pterodactylus/irc/util/RandomNickname.java @@ -0,0 +1,51 @@ +/* + * XdccDownloader - RandomNickname.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.irc.util; + +import java.util.Random; + +/** + * Creates a random nickname. + * + * @author David ‘Bombe’ Roden + */ +public class RandomNickname { + + /** A random number generator. */ + private static final Random random = new Random(); + + /** + * Creates a new random nickname. + * + * @return A new random nickname + */ + public static String get() { + StringBuilder nickname = new StringBuilder(9); + + for (int index = 0; index < 9; ++index) { + char letter = (char) ('A' + random.nextInt(26 + 26)); + if (letter > 'Z') { + letter += 'a' - 'Z' - 1; + } + nickname.append(letter); + } + + return nickname.toString(); + } + +} -- 2.7.4