Modularise! Also add some unit tests.

This commit is contained in:
Major-
2015-05-26 13:49:27 +01:00
parent 902a203861
commit e4778105f5
658 changed files with 1532 additions and 1004 deletions
@@ -0,0 +1,80 @@
package org.apollo.util;
import io.netty.buffer.ByteBuf;
import java.nio.ByteBuffer;
/**
* A utility class which contains {@link ByteBuffer}-related utility methods.
*
* @author Graham
*/
public final class BufferUtil {
/**
* Reads a 'smart' (either a {@code byte} or {@code short} depending on the value) from the specified buffer.
*
* @param buffer The buffer.
* @return The 'smart'.
*/
public static int readSmart(ByteBuffer buffer) {
// Reads a single byte from the buffer without modifying the current position.
int peek = buffer.get(buffer.position()) & 0xFF;
int value = peek > Byte.MAX_VALUE ? (buffer.getShort() & 0xFFFF) + Short.MIN_VALUE : buffer.get() & 0xFF;
return value;
}
/**
* Reads a string from the specified {@link ByteBuffer}.
*
* @param buffer The buffer.
* @return The string.
*/
public static String readString(ByteBuffer buffer) {
StringBuilder bldr = new StringBuilder();
char character;
while ((character = (char) buffer.get()) != BufferUtil.STRING_TERMINATOR) {
bldr.append(character);
}
return bldr.toString();
}
/**
* Reads a string from the specified {@link ByteBuf}.
*
* @param buffer The buffer.
* @return The string.
*/
public static String readString(ByteBuf buffer) {
StringBuilder builder = new StringBuilder();
int character;
while (buffer.isReadable() && (character = buffer.readUnsignedByte()) != BufferUtil.STRING_TERMINATOR) {
builder.append((char) character);
}
return builder.toString();
}
/**
* Reads a 24-bit medium integer from the specified {@link ByteBuffer}s current position and increases the buffers
* position by 3.
*
* @param buffer The {@link ByteBuffer} to read from.
* @return The read 24-bit medium integer.
*/
public static int readUnsignedMedium(ByteBuffer buffer) {
return (buffer.getShort() & 0xFFFF) << 8 | buffer.get() & 0xFF;
}
/**
* The terminator of a string.
*/
public static final int STRING_TERMINATOR = 10;
/**
* Default private constructor to prevent instantiation.
*/
private BufferUtil() {
}
}
@@ -0,0 +1,40 @@
package org.apollo.util;
import java.util.Collection;
import java.util.Queue;
import java.util.function.Consumer;
import com.google.common.base.Preconditions;
/**
* A utility class containing helper methods for various {@link Collection} objects.
*
* @author Ryley
*/
public final class CollectionUtil {
/**
* Polls every element within the specified {@link Queue} and performs the specified {@link Consumer} event for each
* element.
*
* @param queue The Queue to poll each element for, may not be {@code null}.
* @param consumer The Consumer event to execute for each polled element, may not be {@code null}.
*/
public static <T> void pollAll(Queue<T> queue, Consumer<T> consumer) {
Preconditions.checkNotNull(queue, "Queue may not be null");
Preconditions.checkNotNull(consumer, "Consumer may not be null");
T element;
while ((element = queue.poll()) != null) {
consumer.accept(element);
}
}
/**
* Suppresses the default public constructor to discourage normal instantiation outside of this class.
*/
private CollectionUtil() {
}
}
@@ -0,0 +1,124 @@
package org.apollo.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
/**
* A utility class for performing compression/decompression.
*
* @author Graham
*/
public final class CompressionUtil {
/**
* Bzip2s the specified array, removing the header.
*
* @param uncompressed The uncompressed array.
* @return The compressed array.
* @throws IOException If there is an error compressing the array.
*/
public static byte[] bzip2(byte[] uncompressed) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
try (BZip2CompressorOutputStream os = new BZip2CompressorOutputStream(bout, 1)) {
os.write(uncompressed);
os.finish();
byte[] compressed = bout.toByteArray();
byte[] newCompressed = new byte[compressed.length - 4]; // Strip the header
System.arraycopy(compressed, 4, newCompressed, 0, newCompressed.length);
return newCompressed;
}
}
/**
* Gzips the specified array.
*
* @param uncompressed The uncompressed array.
* @return The compressed array.
* @throws IOException If there is an error compressing the array.
*/
public static byte[] gzip(byte[] uncompressed) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
try (DeflaterOutputStream os = new GZIPOutputStream(bout)) {
os.write(uncompressed);
os.finish();
return bout.toByteArray();
}
}
/**
* Debzip2s the compressed array and places the result into the decompressed array.
*
* @param compressed The compressed array, <strong>without</strong> the header.
* @param decompressed The decompressed array.
* @throws IOException If there is an error decompressing the array.
*/
public static void debzip2(byte[] compressed, byte[] decompressed) throws IOException {
byte[] newCompressed = new byte[compressed.length + 4];
newCompressed[0] = 'B';
newCompressed[1] = 'Z';
newCompressed[2] = 'h';
newCompressed[3] = '1';
System.arraycopy(compressed, 0, newCompressed, 4, compressed.length);
try (DataInputStream is = new DataInputStream(new BZip2CompressorInputStream(new ByteArrayInputStream(newCompressed)))) {
is.readFully(decompressed);
}
}
/**
* Degzips the compressed array and places the results into the decompressed array.
*
* @param compressed The compressed array.
* @param decompressed The decompressed array.
* @throws IOException If an I/O error occurs.
*/
public static void degzip(byte[] compressed, byte[] decompressed) throws IOException {
try (DataInputStream is = new DataInputStream(new GZIPInputStream(new ByteArrayInputStream(compressed)))) {
is.readFully(decompressed);
}
}
/**
* Degzips the compressed {@link ByteBuffer} and returns the result as a byte array.
*
* @param compressed The compressed buffer.
* @return The decompressed array.
* @throws IOException If there is an error decompressing the buffer.
*/
public static byte[] degzip(ByteBuffer compressed) throws IOException {
byte[] data = new byte[compressed.remaining()];
compressed.get(data);
try (InputStream is = new GZIPInputStream(new ByteArrayInputStream(data)); ByteArrayOutputStream os = new ByteArrayOutputStream()) {
while (true) {
byte[] buf = new byte[1024];
int read = is.read(buf, 0, buf.length);
if (read == -1) {
break;
}
os.write(buf, 0, read);
}
return os.toByteArray();
}
}
/**
* Default private constructor to prevent instantiation.
*/
private CompressionUtil() {
}
}
@@ -0,0 +1,48 @@
package org.apollo.util;
import java.util.Enumeration;
import java.util.Iterator;
/**
* A utility class for wrapping old {@link Enumeration} objects inside an {@link Iterator} to allow for greater
* compatibility.
*
* @author Graham
*/
public final class EnumerationUtil {
/**
* Returns an {@link Iterator} which wraps around the specified {@link Enumeration}.
*
* @param enumeration The {@link Enumeration}.
* @return An {@link Iterator}.
*/
public static <E> Iterator<E> asIterator(final Enumeration<E> enumeration) {
return new Iterator<E>() {
@Override
public boolean hasNext() {
return enumeration.hasMoreElements();
}
@Override
public E next() {
return enumeration.nextElement();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Cannot remove an element using this wrapper.");
}
};
}
/**
* Default private constructor to prevent instantiation by other classes.
*/
private EnumerationUtil() {
}
}
@@ -0,0 +1,52 @@
package org.apollo.util;
/**
* Contains language-related utility methods.
*
* @author Graham
* @author Major
*/
public final class LanguageUtil {
/**
* Gets the indefinite article of the specified String.
*
* @param string The String.
* @return The indefinite article.
*/
public static String getIndefiniteArticle(String string) {
char first = Character.toLowerCase(string.charAt(0));
if (allUpperCase(string)) {
if (first == 'f' || first == 'l' | first == 'm' || first == 'n' || first == 's') {
return "an";
}
}
boolean vowel = first == 'a' || first == 'e' || first == 'i' || first == 'o' || first == 'u';
return vowel ? "an" : "a";
}
/**
* Returns whether or not the each letter in the specified String is upper case (i.e. digits etc are ignored).
*
* @param string The string.
* @return {@code true} if no letters in the specified String are lower case, otherwise {@code false}.
*/
private static boolean allUpperCase(String string) {
for (char character : string.toCharArray()) {
if (Character.isLowerCase(character)) {
return false;
}
}
return true;
}
/**
* Sole private constructor to prevent instantiation.
*/
private LanguageUtil() {
}
}
@@ -0,0 +1,72 @@
package org.apollo.util;
import com.google.common.base.Preconditions;
/**
* A class which contains name-related utility methods.
*
* @author Graham
*/
public final class NameUtil {
/**
* An array of valid characters in a player name encoded as a long.
*/
private static final char[] NAME_CHARS = { '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '+', '=', ':', ';', '.', '>', '<', ',',
'"', '[', ']', '|', '?', '/', '`' };
/**
* Converts a long to a player name.
*
* @param l The long.
* @return The player name.
*/
public static String decodeBase37(long l) {
int i = 0;
char[] chars = new char[12];
while (l != 0L) {
long tmp = l;
l /= 37L;
chars[11 - i++] = NAME_CHARS[(int) (tmp - l * 37L)];
}
return new String(chars, 12 - i, i);
}
/**
* Converts a player name to a long.
*
* @param name The player name.
* @return The long.
*/
public static long encodeBase37(String name) {
Preconditions.checkArgument(name.length() <= 12, "Name too long.");
long l = 0;
for (int index = 0; index < name.length(); index++) {
char c = name.charAt(index);
l *= 37;
if (c >= 'A' && c <= 'Z') {
l += 1 + c - 65;
} else if (c >= 'a' && c <= 'z') {
l += 1 + c - 97;
} else if (c >= '0' && c <= '9') {
l += 27 + c - 48;
}
}
for (; l % 37L == 0L && l != 0L; l /= 37L) {
;
}
return l;
}
/**
* Default private constructor to prevent instantiation.
*/
private NameUtil() {
}
}
+49
View File
@@ -0,0 +1,49 @@
package org.apollo.util;
/**
* Represents a point on a 2-dimensional Cartesian plane.
*
* @author Major
*/
public final class Point {
/**
* The x coordinate.
*/
private final int x;
/**
* The y coordinate.
*/
private final int y;
/**
* Creates a new point with the specified coordinates.
*
* @param x The x coordinate.
* @param y The y coordinate.
*/
public Point(int x, int y) {
this.x = x;
this.y = y;
}
/**
* Gets the x coordinate of this point.
*
* @return The x coordinate.
*/
public int getX() {
return x;
}
/**
* Gets the y coordinate of this point.
*
* @return The y coordinate.
*/
public int getY() {
return y;
}
}
@@ -0,0 +1,67 @@
package org.apollo.util;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
import java.util.Objects;
/**
* A stateful implementation of a {@link ByteToMessageDecoder} which may be extended and used by other classes. The
* current state is tracked by this class and is a user-specified enumeration.
*
* The state may be changed by calling the {@link StatefulFrameDecoder#setState} method.
*
* The current state is supplied as a parameter in the {@link StatefulFrameDecoder#decode} and
* {@link StatefulFrameDecoder#decodeLast} methods.
*
* This class is not thread safe: it is recommended that the state is only set in the decode methods overridden.
*
* @author Graham
* @param <T> The state enumeration.
*/
public abstract class StatefulFrameDecoder<T extends Enum<T>> extends ByteToMessageDecoder {
/**
* The current state.
*/
private T state;
/**
* Creates the stateful frame decoder with the specified initial state.
*
* @param state The initial state.
* @throws NullPointerException If the state is {@code null}.
*/
public StatefulFrameDecoder(T state) {
setState(state);
}
/**
* Sets a new state.
*
* @param state The new state.
* @throws NullPointerException If the state is {@code null}.
*/
public final void setState(T state) {
this.state = Objects.requireNonNull(state, "State cannot be null.");
}
@Override
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
decode(ctx, in, out, state);
}
/**
* Decodes the received packets into a frame.
*
* @param ctx The current context of this handler.
* @param in The cumulative buffer, which may contain zero or more bytes.
* @param out The {@link List} of objects to pass forward through the pipeline.
* @param state The current state. The state may be changed by calling {@link #setState}.
* @throws Exception If there is an exception when decoding a frame.
*/
protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out, T state) throws Exception;
}
@@ -0,0 +1,51 @@
package org.apollo.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* A class which contains {@link InputStream}- and {@link OutputStream}-related utility methods.
*
* @author Graham
*/
public final class StreamUtil {
/**
* Reads a string from the specified input stream.
*
* @param is The input stream.
* @return The string.
* @throws IOException If an I/O error occurs.
*/
public static String readString(InputStream is) throws IOException {
StringBuilder builder = new StringBuilder();
char character;
while ((character = (char) is.read()) != -1 && character != '\0') {
builder.append(character);
}
return builder.toString();
}
/**
* Writes a string to the specified output stream.
*
* @param os The output stream.
* @param str The string.
* @throws IOException If an I/O error occurs.
*/
public static void writeString(OutputStream os, String str) throws IOException {
for (char c : str.toCharArray()) {
os.write(c);
}
os.write('\0');
}
/**
* Default private constructor to prevent instantiation.
*/
private StreamUtil() {
}
}
+142
View File
@@ -0,0 +1,142 @@
package org.apollo.util;
/**
* A class which contains text-related utility methods.
*
* @author Graham
*/
public final class TextUtil {
/**
* An array of characters ordered by frequency - the elements with lower indices (generally) appear more often in
* chat messages.
*/
public static final char[] FREQUENCY_ORDERED_CHARS = { ' ', 'e', 't', 'a', 'o', 'i', 'h', 'n', 's', 'r', 'd', 'l',
'u', 'm', 'w', 'c', 'y', 'f', 'g', 'p', 'b', 'v', 'k', 'x', 'j', 'q', 'z', '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', ' ', '!', '?', '.', ',', ':', ';', '(', ')', '-', '&', '*', '\\', '\'', '@', '#', '+',
'=', '\243', '$', '%', '"', '[', ']' };
/**
* Capitalizes the string correctly.
*
* @param str The input string.
* @return The string with correct capitalization.
*/
public static String capitalize(String str) {
boolean capitalize = true;
StringBuilder bldr = new StringBuilder(str);
for (int index = 0, length = str.length(); index < length; index++) {
char character = bldr.charAt(index);
if (character == '.' || character == '!' || character == '?') {
capitalize = true;
} else if (capitalize && !Character.isWhitespace(character)) {
bldr.setCharAt(index, Character.toUpperCase(character));
capitalize = false;
}
}
return bldr.toString();
}
/**
* Compresses the input text ({@code in}) and places the result in the {@code out} array.
*
* @param in The input text.
* @param out The output array.
* @return The number of bytes written to the output array.
*/
public static int compress(String in, byte[] out) {
if (in.length() > 80) {
in = in.substring(0, 80);
}
in = in.toLowerCase();
int carry = -1;
int outPos = 0;
for (int inPos = 0; inPos < in.length(); inPos++) {
char c = in.charAt(inPos);
int tblPos = 0;
for (int i = 0; i < FREQUENCY_ORDERED_CHARS.length; i++) {
if (c == FREQUENCY_ORDERED_CHARS[i]) {
tblPos = i;
break;
}
}
if (tblPos > 12) {
tblPos += 195;
}
if (carry == -1) {
if (tblPos < 13) {
carry = tblPos;
} else {
out[outPos++] = (byte) tblPos;
}
} else if (tblPos < 13) {
out[outPos++] = (byte) ((carry << 4) + tblPos);
carry = -1;
} else {
out[outPos++] = (byte) ((carry << 4) + (tblPos >> 4));
carry = tblPos & 0xF;
}
}
if (carry != -1) {
out[outPos++] = (byte) (carry << 4);
}
return outPos;
}
/**
* Filters invalid characters from the specified string.
*
* @param str The input string.
* @return The filtered string.
*/
public static String filterInvalidCharacters(String str) {
StringBuilder builder = new StringBuilder();
for (char c : str.toLowerCase().toCharArray()) {
for (char validChar : FREQUENCY_ORDERED_CHARS) {
if (c == validChar) {
builder.append(c);
break;
}
}
}
return builder.toString();
}
/**
* Uncompresses the compressed data ({@code in}) with the length ({@code len}) and returns the uncompressed
* {@link String}.
*
* @param in The compressed input data.
* @param len The length.
* @return The uncompressed {@link String}.
*/
public static String decompress(byte[] in, int len) {
byte[] out = new byte[4096];
int outPos = 0;
int carry = -1;
for (int i = 0; i < len * 2; i++) {
int tblPos = in[i / 2] >> 4 - 4 * (i % 2) & 0xF;
if (carry == -1) {
if (tblPos < 13) {
out[outPos++] = (byte) FREQUENCY_ORDERED_CHARS[tblPos];
} else {
carry = tblPos;
}
} else {
out[outPos++] = (byte) FREQUENCY_ORDERED_CHARS[(carry << 4) + tblPos - 195];
carry = -1;
}
}
return new String(out, 0, outPos);
}
/**
* Default private constructor to prevent instantiation.
*/
private TextUtil() {
}
}
@@ -0,0 +1,87 @@
package org.apollo.util;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Objects;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
/**
* A static utility class which provides ease of use functionality for {@link Thread}s
*
* @author Ryley
* @author Major
*/
public final class ThreadUtil {
/**
* Returns the amount of available processors available to the Java virtual machine.
*/
public static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
/**
* A {@link Logger} used to debug messages to the console.
*/
private static final Logger LOGGER = Logger.getLogger(ThreadUtil.class.getSimpleName());
/**
* The default {@link UncaughtExceptionHandler} which raises an error from the logger with the exception and name of
* the specified thread the exception occurred in.
*/
private static final UncaughtExceptionHandler DEFAULT_EXCEPTION_HANDLER = (thread, exception) -> LOGGER.log(Level.SEVERE,
"Exception occured in thread " + thread.getName(), exception);
/**
* Builds a {@link ThreadFactory} using the specified {@code String} name-format, normal thread priority and the
* default {@link UncaughtExceptionHandler}.
*
* @see #DEFAULT_EXCEPTION_HANDLER
*
* @param name The name-format used when creating threads, may not be {@code null}.
* @return A new {@link ThreadFactory} from the specified parameters, never {@code null}.
*/
public static ThreadFactory create(String name) {
return create(name, Thread.NORM_PRIORITY, DEFAULT_EXCEPTION_HANDLER);
}
/**
* Builds a {@link ThreadFactory} using the specified {@code String} name-format, priority and the
* {@link #DEFAULT_EXCEPTION_HANDLER}.
*
* @param name The name-format used when creating threads, may not be {@code null}.
* @param priority The priority used when creating threads.
* @return A new {@link ThreadFactory} from the specified parameters, never {@code null}.
*/
public static ThreadFactory create(String name, int priority) {
return create(name, priority, DEFAULT_EXCEPTION_HANDLER);
}
/**
* Builds a {@link ThreadFactory} using the specified {@code String} name-format, priority and
* {@link UncaughtExceptionHandler}.
*
* @param name The name-format used when creating threads. Must not be {@code null}.
* @param priority The priority used when creating threads.
* @param handler The {@link UncaughtExceptionHandler} used when creating threads. Must not be {@code null}.
* @return A new {@link ThreadFactory} using the specified parameters.
*/
public static ThreadFactory create(String name, int priority, UncaughtExceptionHandler handler) {
Objects.requireNonNull(priority);
ThreadFactoryBuilder builder = new ThreadFactoryBuilder();
builder.setNameFormat(name);
builder.setPriority(priority);
builder.setUncaughtExceptionHandler(handler);
return builder.build();
}
/**
* Sole private constructor to prevent instantiation.
*/
private ThreadUtil() {
}
}
@@ -0,0 +1,4 @@
/**
* Contains utility classes.
*/
package org.apollo.util;
@@ -0,0 +1,287 @@
package org.apollo.util.security;
/**
* <p>
* An implementation of the <a href="http://www.burtleburtle.net/bob/rand/isaacafa.html">ISAAC</a> psuedorandom number
* generator.
* </p>
*
* <pre>
* ------------------------------------------------------------------------------
* Rand.java: By Bob Jenkins. My random number generator, ISAAC.
* rand.init() -- initialize
* rand.val() -- get a random value
* MODIFIED:
* 960327: Creation (addition of randinit, really)
* 970719: use context, not global variables, for internal state
* 980224: Translate to Java
* ------------------------------------------------------------------------------
* </pre>
* <p>
* This class has been changed to be more conformant to Java and javadoc conventions.
* </p>
*
* @author Bob Jenkins
*/
public final class IsaacRandom {
/**
* The golden ratio.
*/
private static final int GOLDEN_RATIO = 0x9e3779b9;
/**
* The log of the size of the result and state arrays.
*/
private static final int LOG_SIZE = Long.BYTES;
/**
* The size of the result and states arrays.
*/
private static final int SIZE = 1 << LOG_SIZE;
/**
* A mask for pseudo-random lookup.
*/
private static int MASK = SIZE - 1 << 2;
/**
* The results given to the user.
*/
private final int[] results = new int[SIZE];
/**
* The internal state.
*/
private final int[] state = new int[SIZE];
/**
* The count through the results in the results array.
*/
private int count = SIZE;
/**
* The accumulator.
*/
private int accumulator;
/**
* The last result.
*/
private int last;
/**
* The counter.
*/
private int counter;
/**
* Creates the random number generator with the specified seed.
*
* @param seed The seed.
*/
public IsaacRandom(int[] seed) {
int length = Math.min(seed.length, results.length);
System.arraycopy(seed, 0, results, 0, length);
init();
}
/**
* Generates 256 results.
*/
private void isaac() {
int i, j, x, y;
last += ++counter;
for (i = 0, j = SIZE / 2; i < SIZE / 2;) {
x = state[i];
accumulator ^= accumulator << 13;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
x = state[i];
accumulator ^= accumulator >>> 6;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
x = state[i];
accumulator ^= accumulator << 2;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
x = state[i];
accumulator ^= accumulator >>> 16;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
}
for (j = 0; j < SIZE / 2;) {
x = state[i];
accumulator ^= accumulator << 13;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
x = state[i];
accumulator ^= accumulator >>> 6;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
x = state[i];
accumulator ^= accumulator << 2;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
x = state[i];
accumulator ^= accumulator >>> 16;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
}
}
/**
* Initializes this random number generator.
*/
private void init() {
int i;
int a, b, c, d, e, f, g, h;
a = b = c = d = e = f = g = h = GOLDEN_RATIO;
for (i = 0; i < 4; ++i) {
a ^= b << 11;
d += a;
b += c;
b ^= c >>> 2;
e += b;
c += d;
c ^= d << 8;
f += c;
d += e;
d ^= e >>> 16;
g += d;
e += f;
e ^= f << 10;
h += e;
f += g;
f ^= g >>> 4;
a += f;
g += h;
g ^= h << 8;
b += g;
h += a;
h ^= a >>> 9;
c += h;
a += b;
}
for (i = 0; i < SIZE; i += 8) { /* fill in mem[] with messy stuff */
a += results[i];
b += results[i + 1];
c += results[i + 2];
d += results[i + 3];
e += results[i + 4];
f += results[i + 5];
g += results[i + 6];
h += results[i + 7];
a ^= b << 11;
d += a;
b += c;
b ^= c >>> 2;
e += b;
c += d;
c ^= d << 8;
f += c;
d += e;
d ^= e >>> 16;
g += d;
e += f;
e ^= f << 10;
h += e;
f += g;
f ^= g >>> 4;
a += f;
g += h;
g ^= h << 8;
b += g;
h += a;
h ^= a >>> 9;
c += h;
a += b;
state[i] = a;
state[i + 1] = b;
state[i + 2] = c;
state[i + 3] = d;
state[i + 4] = e;
state[i + 5] = f;
state[i + 6] = g;
state[i + 7] = h;
}
for (i = 0; i < SIZE; i += 8) {
a += state[i];
b += state[i + 1];
c += state[i + 2];
d += state[i + 3];
e += state[i + 4];
f += state[i + 5];
g += state[i + 6];
h += state[i + 7];
a ^= b << 11;
d += a;
b += c;
b ^= c >>> 2;
e += b;
c += d;
c ^= d << 8;
f += c;
d += e;
d ^= e >>> 16;
g += d;
e += f;
e ^= f << 10;
h += e;
f += g;
f ^= g >>> 4;
a += f;
g += h;
g ^= h << 8;
b += g;
h += a;
h ^= a >>> 9;
c += h;
a += b;
state[i] = a;
state[i + 1] = b;
state[i + 2] = c;
state[i + 3] = d;
state[i + 4] = e;
state[i + 5] = f;
state[i + 6] = g;
state[i + 7] = h;
}
isaac();
}
/**
* Gets the next random value.
*
* @return The next random value.
*/
public int nextInt() {
if (0 == count--) {
isaac();
count = SIZE - 1;
}
return results[count];
}
}
@@ -0,0 +1,51 @@
package org.apollo.util.security;
/**
* A pair of two {@link IsaacRandom} random number generators used as a stream cipher. One takes the role of an encoder
* for this endpoint, the other takes the role of a decoder for this endpoint.
*
* @author Graham
*/
public final class IsaacRandomPair {
/**
* The random number generator used to decode data.
*/
private final IsaacRandom decodingRandom;
/**
* The random number generator used to encode data.
*/
private final IsaacRandom encodingRandom;
/**
* Creates the pair of random number generators.
*
* @param encodingRandom The random number generator used for encoding.
* @param decodingRandom The random number generator used for decoding.
*/
public IsaacRandomPair(IsaacRandom encodingRandom, IsaacRandom decodingRandom) {
this.encodingRandom = encodingRandom;
this.decodingRandom = decodingRandom;
}
/**
* Gets the random number generator used for decoding.
*
* @return The random number generator used for decoding.
*/
public IsaacRandom getDecodingRandom() {
return decodingRandom;
}
/**
* Gets the random number generator used for encoding.
*
* @return The random number generator used for encoding.
*/
public IsaacRandom getEncodingRandom() {
return encodingRandom;
}
}
@@ -0,0 +1,138 @@
package org.apollo.util.security;
import org.apollo.util.NameUtil;
/**
* Holds the credentials for a player.
*
* @author Graham
*/
public final class PlayerCredentials {
/**
* The player's username encoded as a long.
*/
private final long encodedUsername;
/**
* The player's password.
*/
private String password;
/**
* The computer's unique identifier.
*/
private final int uid;
/**
* The player's username.
*/
private final String username;
/**
* The hash of the player's username.
*/
private final int usernameHash;
/**
* The Player's host address, represented as a String.
*/
private final String hostAddress;
/**
* Creates a new {@link PlayerCredentials} object with the specified name, password and uid.
*
* @param username The player's username.
* @param password The player's password.
* @param usernameHash The hash of the player's username.
* @param uid The computer's uid.
* @param hostAddress The Player's connecting host address.
*/
public PlayerCredentials(String username, String password, int usernameHash, int uid, String hostAddress) {
this.username = username;
encodedUsername = NameUtil.encodeBase37(username);
this.password = password;
this.usernameHash = usernameHash;
this.uid = uid;
this.hostAddress = hostAddress;
}
/**
* Gets the player's username encoded as a long.
*
* @return The username as encoded by {@link NameUtil#encodeBase37(String)}.
*/
public long getEncodedUsername() {
return encodedUsername;
}
/**
* Sets the player's password
*
* @param password The player's new password
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Gets the player's password.
*
* @return The player's password.
*/
public String getPassword() {
return password;
}
/**
* Gets the computer's uid.
*
* @return The computer's uid.
*/
public int getUid() {
return uid;
}
/**
* Gets the player's username.
*
* @return The player's username.
*/
public String getUsername() {
return username;
}
/**
* Gets the username hash.
*
* @return The username hash.
*/
public int getUsernameHash() {
return usernameHash;
}
/**
* Gets the Player's connecting host address.
*
* @return The Player's host address, represented as a String.
*/
public String getHostAddress() {
return hostAddress;
}
@Override
public int hashCode() {
return Long.hashCode(encodedUsername);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PlayerCredentials) {
PlayerCredentials other = (PlayerCredentials) obj;
return encodedUsername == other.encodedUsername;
}
return false;
}
}
@@ -0,0 +1,4 @@
/**
* Contains classes related to security and cryptography.
*/
package org.apollo.util.security;
@@ -0,0 +1,102 @@
package org.apollo.util.tools;
/**
* Contains equipment name constants.
*
* @author Graham
* @author Palidino76
*/
public final class EquipmentConstants {
/**
* Amulets.
*/
public static final String[] AMULETS = { "amulet", "necklace", "Amulet of" };
/**
* Arrows.
*/
public static final String[] ARROWS = { "arrow", "arrows", "arrow(p)", "arrow(+)", "arrow(s)", "bolt", "Bolt rack",
"Opal bolts", "Dragon bolts" };
/**
* Bodies.
*/
public static final String[] BODY = { "platebody", "chainbody", "robetop", "leathertop", "platemail", "top",
"brassard", "Robe top", "body", "platebody (t)", "platebody (g)", "body(g)", "body_(g)", "chestplate",
"torso", "shirt" };
/**
* Boots.
*/
public static final String[] BOOTS = { "boots", "Boots" };
/**
* Capes.
*/
public static final String[] CAPES = { "cape", "Cape" };
/**
* Full bodies.
*/
public static final String[] FULL_BODIES = { "top", "shirt", "platebody", "Ahrims robetop", "Karils leathertop",
"brassard", "Robe top", "robetop", "platebody (t)", "platebody (g)", "chestplate", "torso" };
/**
* Full hats.
*/
public static final String[] FULL_HATS = { "med helm", "coif", "Dharoks helm", "hood", "Initiate helm", "Coif",
"Helm of neitiznot" };
/**
* Full masks.
*/
public static final String[] FULL_MASKS = { "full helm", "mask", "Veracs helm", "Guthans helm", "Torags helm",
"Karils coif", "full helm (t)", "full helm (g)", "mask" };
/**
* Gloves.
*/
public static final String[] GLOVES = { "gloves", "gauntlets", "Gloves", "vambraces", "vamb", "bracers" };
/**
* Hats.
*/
public static final String[] HATS = { "tiara", "helm", "hood", "coif", "Coif", "hat", "partyhat", "Hat",
"full helm (t)", "full helm (g)", "hat (t)", "hat (g)", "cav", "boater", "helmet", "mask",
"Helm of neitiznot" };
/**
* Legs.
*/
public static final String[] LEGS = { "platelegs", "plateskirt", "skirt", "bottoms", "chaps", "platelegs (t)",
"platelegs (g)", "bottom", "skirt", "skirt (g)", "skirt (t)", "chaps (g)", "chaps (t)", "tassets", "legs",
"Flared trousers" };
/**
* Rings.
*/
public static final String[] RINGS = { "ring", "Ring of" };
/**
* Shields.
*/
public static final String[] SHIELDS = { "kiteshield", "sq shield", "Toktz-ket", "books", "book", "kiteshield (t)",
"kiteshield (g)", "kiteshield(h)", "defender", "shield" };
/**
* Weapons.
*/
public static final String[] WEAPONS = { "scimitar", "longsword", "sword", "longbow", "shortbow", "dagger", "mace",
"halberd", "spear", "Abyssal whip", "axe", "flail", "crossbow", "Torags hammers", "dagger(p)", "dagger(+)",
"dagger(s)", "spear(p)", "spear(+)", "spear(s)", "spear(kp)", "maul", "dart", "dart(p)", "javelin",
"javelin(p)", "knife", "knife(p)", "Longbow", "Shortbow", "Crossbow", "Toktz-xil", "Toktz-mej",
"Tzhaar-ket", "staff", "Staff", "godsword", "c'bow", "Crystal bow", "Dark bow", "Magic butterfly net" };
/**
* Default private constructor to prevent instantiation.
*/
private EquipmentConstants() {
}
}
@@ -0,0 +1,45 @@
package org.apollo.util.tools;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Random;
/**
* An RSA key generator.
*
* @author Graham
* @author Major
*/
public final class RsaKeyGenerator {
/**
* The bit count. <strong>Strongly</strong> recommended to be at least 2,048.
*/
private static final int BIT_COUNT = 2_048;
/**
* The entry point of the RsaKeyGenerator.
*
* @param args The application arguments.
*/
public static void main(String[] args) {
Random random = new SecureRandom();
BigInteger publicKey = BigInteger.valueOf(65_537);
BigInteger p, q, phi, modulus, privateKey;
do {
p = BigInteger.probablePrime(BIT_COUNT / 2, random);
q = BigInteger.probablePrime(BIT_COUNT / 2, random);
phi = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
modulus = p.multiply(q);
privateKey = publicKey.modInverse(phi);
} while (modulus.bitLength() != BIT_COUNT || privateKey.bitLength() != BIT_COUNT || !phi.gcd(publicKey).equals(BigInteger.ONE));
System.out.println("modulus: " + modulus);
System.out.println("public key: " + publicKey);
System.out.println("private key: " + privateKey);
}
}
@@ -0,0 +1,4 @@
/**
* Contains several stand-alone utilities.
*/
package org.apollo.util.tools;
@@ -0,0 +1,247 @@
package org.apollo.util.xml;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
* A class which represents a single node in the DOM tree, maintaining information about its children, attributes, value
* and name.
*
* @author Graham
*/
public final class XmlNode implements Iterable<XmlNode> {
/**
* The attribute map.
*/
private final Map<String, String> attributes = new HashMap<>();
/**
* The list of child nodes.
*/
private final List<XmlNode> children = new ArrayList<>();
/**
* The name of this node.
*/
private String name;
/**
* The value of this node, or {@code null} if it has no value.
*/
private String value;
/**
* Creates a new {@link XmlNode} with the specified name.
*
* @param name The name of this node.
*/
public XmlNode(String name) {
this.name = name;
}
/**
* Adds a child {@link XmlNode}.
*
* @param child The child to add.
*/
public void addChild(XmlNode child) {
children.add(child);
}
/**
* Checks if an attribute with the specified name exists.
*
* @param name The attribute's name.
* @return {@code true} if an attribute with that name exists, {@code false} otherwise.
*/
public boolean containsAttribute(String name) {
return attributes.containsKey(name);
}
/**
* Gets an attribute by it's name.
*
* @param name The name of the attribute.
* @return The attribute's value, or {@code null} if it doesn't exist.
*/
public String getAttribute(String name) {
return attributes.get(name);
}
/**
* Gets the attribute count.
*
* @return The number of attributes.
*/
public int getAttributeCount() {
return attributes.size();
}
/**
* Gets a {@link Set} of attribute names.
*
* @return The set of names.
*/
public Set<String> getAttributeNames() {
return attributes.keySet();
}
/**
* Gets a {@link Set} of attribute map entries.
*
* @return The set of entries.
*/
public Set<Map.Entry<String, String>> getAttributes() {
return attributes.entrySet();
}
/**
* Gets the first child with the specified name.
*
* @param name The name of the child.
* @return The {@link XmlNode} if a child was found with a matching name, {@code null} otherwise.
*/
public XmlNode getChild(String name) {
for (XmlNode child : children) {
if (child.getName().equals(name)) {
return child;
}
}
return null;
}
/**
* Gets the child count.
*
* @return The number of child {@link XmlNode}s.
*/
public int getChildCount() {
return children.size();
}
/**
* Gets a {@link Collection} of child {@link XmlNode}s.
*
* @return The collection.
*/
public Collection<XmlNode> getChildren() {
return Collections.unmodifiableCollection(children);
}
/**
* Gets the name of this node.
*
* @return The name of this node.
*/
public String getName() {
return name;
}
/**
* Gets the value of this node.
*
* @return The value of this node, or {@code null} if it has no value.
*/
public String getValue() {
return value;
}
/**
* Gets the value of this node, wrapped in an {@link Optional}
*
* @return The value of this node if it exists otherwise {@link Optional#empty()} is returned
*/
public Optional<String> getOptionalValue() {
return Optional.ofNullable(value);
}
/**
* Checks if this node has a value.
*
* @return {@code true} if so, {@code false} if not.
*/
public boolean hasValue() {
return value != null;
}
@Override
public Iterator<XmlNode> iterator() {
return children.iterator();
}
/**
* Removes all attributes.
*/
public void removeAllAttributes() {
attributes.clear();
}
/**
* Removes all children.
*/
public void removeAllChildren() {
children.clear();
}
/**
* Removes an attribute.
*
* @param name The name of the attribute.
*/
public void removeAttribute(String name) {
attributes.remove(name);
}
/**
* Removes a child {@link XmlNode}.
*
* @param child The child to remove.
*/
public void removeChild(XmlNode child) {
children.remove(child);
}
/**
* Removes the value of this node.
*/
public void removeValue() {
value = null;
}
/**
* Adds an attribute. It will overwrite an existing attribute if it exists.
*
* @param name The name of the attribute.
* @param value The value of the attribute.
*/
public void setAttribute(String name, String value) {
attributes.put(name, value);
}
/**
* Sets the name of this node.
*
* @param name The name of this node.
*/
public void setName(String name) {
this.name = name;
}
/**
* Sets the value of this node.
*
* @param value The value of this node.
*/
public void setValue(String value) {
this.value = value;
}
}
@@ -0,0 +1,155 @@
package org.apollo.util.xml;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Stack;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
/**
* A simple XML parser that uses the internal {@link org.xml.sax} API to create a tree of {@link XmlNode} objects.
*
* @author Graham
*/
public final class XmlParser {
/**
* A class which handles SAX events.
*
* @author Graham
*/
private final class XmlHandler extends DefaultHandler {
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
currentNode.setValue(new String(ch, start, length));
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (!nodeStack.isEmpty()) {
currentNode = nodeStack.pop();
}
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
XmlNode next = new XmlNode(localName);
if (rootNode == null) {
rootNode = currentNode = next;
} else {
currentNode.addChild(next);
nodeStack.add(currentNode);
currentNode = next;
}
if (attributes != null) {
int attributeCount = attributes.getLength();
for (int i = 0; i < attributeCount; i++) {
String attribLocalName = attributes.getLocalName(i);
currentNode.setAttribute(attribLocalName, attributes.getValue(i));
}
}
}
}
/**
* The current node.
*/
private XmlNode currentNode;
/**
* The SAX event handler.
*/
private final XmlHandler eventHandler;
/**
* The stack of nodes, used when traversing the document and going through child nodes.
*/
private final Stack<XmlNode> nodeStack = new Stack<>();
/**
* The current root node.
*/
private XmlNode rootNode;
/**
* The {@link XMLReader} backing this {@link XmlParser}.
*/
private final XMLReader xmlReader;
/**
* Creates the XML parser.
*
* @throws SAXException If a SAX error occurs.
*/
public XmlParser() throws SAXException {
xmlReader = XMLReaderFactory.createXMLReader();
eventHandler = this.new XmlHandler();
init();
}
/**
* Initialises this parser.
*/
private void init() {
xmlReader.setContentHandler(eventHandler);
xmlReader.setDTDHandler(eventHandler);
xmlReader.setEntityResolver(eventHandler);
xmlReader.setErrorHandler(eventHandler);
}
/**
* Parses XML data from the {@link InputSource}.
*
* @param source The {@link InputSource}.
* @return The root {@link XmlNode}.
* @throws IOException If an I/O error occurs.
* @throws SAXException If a SAX error occurs.
*/
private XmlNode parse(InputSource source) throws IOException, SAXException {
rootNode = null;
xmlReader.parse(source);
if (rootNode == null) {
throw new SAXException("No root element.");
}
return rootNode;
}
/**
* Parses XML data from the given {@link InputStream}.
*
* @param is The {@link InputStream}.
* @return The root {@link XmlNode}.
* @throws IOException If an I/O error occurs.
* @throws SAXException If a SAX error occurs.
*/
public XmlNode parse(InputStream is) throws IOException, SAXException {
synchronized (this) {
return parse(new InputSource(is));
}
}
/**
* Parses XML data from the given {@link Reader}.
*
* @param reader The {@link Reader}.
* @return The root {@link XmlNode}.
* @throws IOException If an I/O error occurs.
* @throws SAXException If a SAX error occurs.
*/
public XmlNode parse(Reader reader) throws IOException, SAXException {
synchronized (this) {
return parse(new InputSource(reader));
}
}
}
@@ -0,0 +1,4 @@
/**
* Contains classes which parse XML data into an object tree.
*/
package org.apollo.util.xml;
@@ -0,0 +1,49 @@
package org.apollo.util;
import static org.junit.Assert.assertEquals;
import java.nio.ByteBuffer;
import org.junit.Test;
/**
* Contains tests for {@link BufferUtil}.
*
* @author Graham
*/
public class TestBufferUtil {
/**
* Tests the {@link BufferUtil#readUnsignedMedium} method.
*/
@Test
public void testReadUnsignedTriByte() {
ByteBuffer buf = ByteBuffer.allocate(3);
buf.put((byte) 123);
buf.put((byte) 45);
buf.put((byte) 67);
buf.flip();
assertEquals(8072515, BufferUtil.readUnsignedMedium(buf));
}
/**
* Tests the {@link BufferUtil#readString(ByteBuffer)} method.
*/
@Test
public void testReadString() {
ByteBuffer buf = ByteBuffer.allocate(8);
buf.put((byte) 'h');
buf.put((byte) 'e');
buf.put((byte) 'l');
buf.put((byte) 'l');
buf.put((byte) 'o');
buf.put((byte) BufferUtil.STRING_TERMINATOR);
buf.put((byte) 66);
buf.put((byte) 6);
buf.flip();
assertEquals("hello", BufferUtil.readString(buf));
}
}
@@ -0,0 +1,43 @@
package org.apollo.util;
import static org.junit.Assert.assertEquals;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.junit.Test;
/**
* Contains tests for {@link ByteBuf} methods in {@link BufferUtil}.
*
* @author Graham
*/
public final class TestByteBufUtil {
/**
* Test the {@link BufferUtil#readString(ByteBuf)} method.
*/
@Test
public void testReadString() {
ByteBuf buf = Unpooled.buffer(6);
buf.writeBytes(new byte[] { 'H', 'e', 'l', 'l', 'o', 10 });
String str = BufferUtil.readString(buf);
assertEquals("Hello", str);
buf = Unpooled.buffer(5);
buf.writeBytes(new byte[] { 'W', 'o', 'r', 'l', 'd' });
str = BufferUtil.readString(buf);
assertEquals("World", str);
buf = Unpooled.buffer(3);
buf.writeByte('!');
buf.writeByte(10);
buf.writeByte('.');
str = BufferUtil.readString(buf);
assertEquals("!", str);
str = BufferUtil.readString(buf);
assertEquals(".", str);
}
}
@@ -0,0 +1,44 @@
package org.apollo.util;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import org.junit.Test;
/**
* Contains tests for {@link CompressionUtil}.
*
* @author Graham
*/
public class TestCompressionUtil {
/**
* Tests the {@link CompressionUtil#bzip2(byte[])} and {@link CompressionUtil#debzip2} methods.
*
* @throws IOException If an I/O error occurs.
*/
@Test
public void testBzip2() throws IOException {
String str = "Hello, World!";
byte[] data = str.getBytes();
byte[] compressed = CompressionUtil.bzip2(data);
CompressionUtil.debzip2(compressed, data);
assertEquals(str, new String(data));
}
/**
* Tests the {@link CompressionUtil#gzip(byte[])} and {@link CompressionUtil#degzip} methods.
*
* @throws IOException If an I/O error occurs.
*/
@Test
public void testGzip() throws IOException {
String str = "Hello, World!";
byte[] data = str.getBytes();
byte[] compressed = CompressionUtil.gzip(data);
CompressionUtil.degzip(compressed, data);
assertEquals(str, new String(data));
}
}
@@ -0,0 +1,25 @@
package org.apollo.util;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
/**
* Contains tests for {@link LanguageUtil}.
*
* @author Graham
*/
public class TestLanguageUtil {
/**
* Tests the {@link LanguageUtil#getIndefiniteArticle} method.
*/
@Test
public void testIndefiniteArticle() {
assertEquals("an", LanguageUtil.getIndefiniteArticle("apple"));
assertEquals("an", LanguageUtil.getIndefiniteArticle("urn"));
assertEquals("a", LanguageUtil.getIndefiniteArticle("nose"));
assertEquals("a", LanguageUtil.getIndefiniteArticle("foot"));
}
}
@@ -0,0 +1,48 @@
package org.apollo.util;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
/**
* A test for the {@link TextUtil} class.
*
* @author Graham
*/
public class TestTextUtil {
/**
* Tests the {@link TextUtil#capitalize} method.
*/
@Test
public void testCapitalize() {
String str = "tHiS is BAD capitAliZation. do You AGreE? YES!";
String capitalized = "This is bad capitalization. Do you agree? Yes!";
assertEquals(capitalized, TextUtil.capitalize(str));
}
/**
* Tests the {@link TextUtil#compress} and {@link TextUtil#decompress} methods.
*/
@Test
public void testCompression() {
String str = "hello, world!";
byte[] compressed = new byte[128];
int len = TextUtil.compress(str, compressed);
String uncompressed = TextUtil.decompress(compressed, len);
assertEquals(str, uncompressed);
}
/**
* Tests the {@link TextUtil#filterInvalidCharacters(String)} method.
*/
@Test
public void testFilter() {
String str = "this contains <<< invalid characters";
String filtered = "this contains invalid characters";
assertEquals(filtered, TextUtil.filterInvalidCharacters(str));
}
}
@@ -0,0 +1,105 @@
package org.apollo.util.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.Set;
import org.junit.Test;
import org.xml.sax.SAXException;
/**
* A test for the {@link XmlParser} class.
*
* @author Graham
*/
public final class TestXmlParser {
/**
* A test for the {@link XmlParser#parse} method.
*
* @throws SAXException If a SAX error occurs.
* @throws IOException If an I/O error occurs.
*/
@Test
public void testParseInputStream() throws SAXException, IOException {
XmlParser parser = new XmlParser();
InputStream input = new ByteArrayInputStream("<root a='1' b='2' c='3'><z><y><x></x></y></z></root>".getBytes());
XmlNode root = parser.parse(input);
assertEquals(root.getName(), "root");
assertEquals(root.getAttributeCount(), 3);
assertEquals(root.getChildCount(), 1);
assertFalse(root.hasValue());
Set<String> names = root.getAttributeNames();
assertTrue(names.contains("a"));
assertTrue(names.contains("b"));
assertTrue(names.contains("c"));
assertFalse(names.contains("z"));
assertFalse(names.contains("y"));
assertFalse(names.contains("x"));
assertEquals("1", root.getAttribute("a"));
assertEquals("2", root.getAttribute("b"));
assertEquals("3", root.getAttribute("c"));
assertNull(root.getAttribute("z"));
assertNull(root.getAttribute("y"));
assertNull(root.getAttribute("x"));
XmlNode[] first = root.getChildren().toArray(new XmlNode[1]);
assertEquals(1, first.length);
assertEquals("z", first[0].getName());
XmlNode[] second = first[0].getChildren().toArray(new XmlNode[1]);
assertEquals(1, second.length);
assertEquals("y", second[0].getName());
XmlNode[] third = second[0].getChildren().toArray(new XmlNode[1]);
assertEquals(1, third.length);
assertEquals("x", third[0].getName());
assertEquals(0, third[0].getChildCount());
}
/**
* A test for the {@link XmlParser#parse(java.io.Reader)} method.
*
* @throws SAXException If a SAX error occurs.
* @throws IOException If an I/O error occurs.
*/
@Test
public void testParseReader() throws SAXException, IOException {
XmlParser parser = new XmlParser();
Reader reader = new StringReader("<alphabet><a>1</a><b>2</b><c>3</c></alphabet>");
XmlNode root = parser.parse(reader);
assertEquals(root.getName(), "alphabet");
assertEquals(root.getAttributeCount(), 0);
assertEquals(root.getChildCount(), 3);
assertFalse(root.hasValue());
XmlNode[] children = root.getChildren().toArray(new XmlNode[3]);
assertEquals(children[0].getName(), "a");
assertEquals(children[1].getName(), "b");
assertEquals(children[2].getName(), "c");
assertEquals(children[0].getValue(), "1");
assertEquals(children[1].getValue(), "2");
assertEquals(children[2].getValue(), "3");
for (int index = 0; index < 3; index++) {
assertTrue(children[index].hasValue());
assertEquals(children[index].getAttributeCount(), 0);
}
}
}