Added java api!

This commit is contained in:
Fredric Silberberg 2014-11-10 17:27:19 -05:00
parent e43cf1703e
commit 0a7c6e52be
19 changed files with 685 additions and 37 deletions

View File

@ -12,19 +12,12 @@ apply plugin: 'java'
// In this section you declare where to find the dependencies of your project
repositories {
// Use 'jcenter' for resolving your dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
mavenCentral()
}
// In this section you declare the dependencies for your production and test code
dependencies {
// The production code uses the SLF4J logging API at compile time
compile 'org.slf4j:slf4j-api:1.7.5'
// Declare the dependency for your favourite test framework you want to use in your tests.
// TestNG is also supported by the Gradle Test task. Just change the
// testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
// 'test.useTestNG()' to your build script.
compile 'com.google.code.gson:gson:2.3'
testCompile 'junit:junit:4.11'
}

View File

@ -1,6 +1,6 @@
#Thu Nov 06 16:30:58 EST 2014
#Thu Nov 06 16:36:42 EST 2014
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-all.zip

View File

@ -1,11 +0,0 @@
/*
* This Java source file was auto generated by running 'gradle buildInit --type java-library'
* by 'fred' at '11/6/14 4:30 PM' with Gradle 2.1
*
* @author fred, @date 11/6/14 4:30 PM
*/
public class Library {
public boolean someLibraryMethod() {
return true;
}
}

View File

@ -0,0 +1,27 @@
package edu.wpi.cs.jsh;
import edu.wpi.cs.jsh.NamedStream;
/**
* Abstract base class that takes care of all of the simple named stream code.
*/
abstract class AbstractBaseStream implements NamedStream {
protected final String m_name;
protected final Class<?> m_type;
protected AbstractBaseStream(String name, Class<?> type) {
m_name = name;
m_type = type;
}
@Override
public String getName() {
return m_name;
}
@Override
public Class<?> getStreamClass() {
return m_type;
}
}

View File

@ -0,0 +1,60 @@
package edu.wpi.cs.jsh;
import java.util.*;
/**
* Created by fred on 11/7/14.
*/
class BasicStream extends AbstractBaseStream implements Stream {
private final Object m_lock;
private final Map<String, NamedStream> m_subStreams = new HashMap<>();
public BasicStream(String name, Object lock) {
super(name, Stream.class);
m_lock = lock;
}
@Override
public <T> TypeStream<T> getTypeStream(String streamName, Class<T> streamType) throws JshException {
synchronized (m_lock) {
TypeStream<T> stream = getStream(streamName, streamType);
if (stream == null) {
stream = JshStreamFactory.getInstance().makeTypeStream(streamName, streamType, m_lock);
m_subStreams.put(streamName, stream);
}
return stream;
}
}
@Override
public Stream getSubStream(String streamName) throws JshException {
synchronized (m_lock) {
Stream stream = getStream(streamName, Stream.class);
if (stream == null) {
stream = JshStreamFactory.getInstance().makeStream(streamName, m_lock, JshStreamFactory.StreamLevel.NORMAL);
m_subStreams.put(streamName, stream);
}
return stream;
}
}
@Override
public Map<String, NamedStream> getSubStreams() {
return m_subStreams;
}
private <S extends NamedStream, T extends Class<?>> S getStream(String name, T type) throws JshException {
S stream = null;
if (m_subStreams.containsKey(name)) {
NamedStream mapStream = m_subStreams.get(name);
if (mapStream.getStreamClass().equals(type)) {
stream = (S) mapStream;
} else {
throw new JshException("Stream " + name + " exists already, and is a different type! Original was "
+ mapStream.getStreamClass() + ", requested was " + type);
}
}
return stream;
}
}

View File

@ -0,0 +1,24 @@
package edu.wpi.cs.jsh;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
/**
* Serializes a {@link BasicStream} according to the JshProtocol
*/
class BasicStreamSerializer implements JsonSerializer<BasicStream> {
@Override
public JsonElement serialize(BasicStream basicStream, Type type, JsonSerializationContext jsonSerializationContext) {
JsonArray output = new JsonArray();
output.add(jsonSerializationContext.serialize(basicStream.getSubStreams()));
return output;
}
}

View File

@ -0,0 +1,50 @@
package edu.wpi.cs.jsh;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
/**
* Created by fred on 11/7/14.
*/
class BasicTypeStream<T> extends AbstractBaseStream implements TypeStream<T> {
private final Object m_lock;
private final Collection<T> m_objects;
public BasicTypeStream(String name, Class<?> type, Object lock) {
super(name, type);
m_lock = lock;
m_objects = new LinkedList<T>();
}
@Override
public void output(T... values) {
if (values != null) {
synchronized (m_lock) {
Collections.addAll(m_objects, values);
try {
JshStreamController.getInstance().incrementCount(values.length);
} catch (JshException e) {
e.printStackTrace();
}
}
}
}
/**
* Gets the objects that have been buffered by this type stream
*
* @return The buffered objects
*/
Collection<T> getObjects() {
return m_objects;
}
/**
* Removes all of the objects currently buffered.
*/
void flushObjects() {
m_objects.clear();
}
}

View File

@ -0,0 +1,18 @@
package edu.wpi.cs.jsh;
import com.google.gson.*;
import com.sun.org.apache.bcel.internal.generic.RET;
import java.lang.reflect.Type;
/**
* Serializes a BasicTypeStream to the proper Jsh format
*/
class BasicTypeStreamSerializer implements JsonSerializer<BasicTypeStream<?>> {
@Override
public JsonElement serialize(BasicTypeStream<?> basicTypeStream, Type type, JsonSerializationContext jsonSerializationContext) {
JsonElement serializedStream = jsonSerializationContext.serialize(basicTypeStream.getObjects());
basicTypeStream.flushObjects();
return serializedStream;
}
}

View File

@ -0,0 +1,14 @@
package edu.wpi.cs.jsh;
/**
* Created by fred on 11/6/14.
*/
public class JshException extends Exception {
private String reason;
public JshException(String reason) {
super(reason);
this.reason = reason;
}
}

View File

@ -0,0 +1,146 @@
package edu.wpi.cs.jsh;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import sun.awt.X11.ToplevelStateListener;
import java.io.PrintStream;
/**
* This factory allows for getting and creation of new streams, getting access to the top level streams, and controlling
* output.
*/
public class JshStreamController {
private static final Object _lock = new Object();
private static final int DEFAULT_MAX_BUFFER = 10;
private static JshStreamController instance;
private static boolean initialized;
/**
* Initializes the JshStream api with the given output sources for stdout and stderr, and the given max buffer size.
* If you want to just use the system default stdout and stderr, call {@link #initialize()} instead. If you want to
* control either the buffer size or the streams individually, call the appropriate initialize method. The
* controller cannot be reinitialized after it has been initialized once, and it will throw an exception if such
* an instance occurs
*
* @param stdOut The printstream to write stdout to
* @param stdErr The printstream to write stderr to
* @throws JshException If the controller is already initialized, or if there is an error setting up the streams
*/
public static void initialize(PrintStream stdOut, PrintStream stdErr, int maxBuffer) throws JshException {
// Argument guards
if (stdOut == null) {
throw new JshException("Given StdOut source was null!");
}
if (stdErr == null) {
throw new JshException("Given StdErr source was null!");
}
synchronized (_lock) {
if (initialized) {
throw new JshException("JshStream is already initialized! Cannot reinitialize in the middle of output.");
}
instance = new JshStreamController(stdOut, stdErr, maxBuffer);
initialized = true;
}
}
/**
* Initializes the JshStream api with the default sysout and syserr, and a given buffer size. If the api has already
* been initialized, then it will throw a JshException
*
* @param maxBuffer The buffer size to use
* @throws JshException If the controller has already been initialized, or if there is an error setting up the stream
*/
public static void initialize(int maxBuffer) throws JshException {
initialize(System.out, System.err, maxBuffer);
}
/**
* Initializes the JshStream api with the given sysout and syserr, and a default buffer size. If the api has already
* been initialized, then it will throw a JshException
*
* @param sysOut The printstream to write stdout to
* @param sysErr The printstream to write stderr to
* @throws JshException If the controller has already been initialized, or if there is an error setting up the stream
*/
public static void initialized(PrintStream sysOut, PrintStream sysErr) throws JshException {
initialize(sysOut, sysErr, DEFAULT_MAX_BUFFER);
}
/**
* Initializes the JshStream api with the default sysout and syserr sources. If the api has already been initialized,
* then it will throw a JshException.
*
* @throws JshException If the controller has already been initialized, or if there is an error setting up the streams
*/
public static void initialize() throws JshException {
initialize(System.out, System.err, DEFAULT_MAX_BUFFER);
}
public static JshStreamController getInstance() throws JshException {
if (!initialized) {
throw new JshException("The JshController has not been set up! Please call one of the initialize methods.");
}
return instance;
}
/**
* Resets the JshStreamController. After calling this, initialize must be called again.
*/
public static void reset() {
synchronized (_lock) {
initialized = false;
instance = null;
}
}
public final Stream StdOutStream;
public final Stream StdErrStream;
private final PrintStream m_stdOutPrinter;
private final PrintStream m_stdErrPrinter;
private final int m_maxBuffer;
private final Gson m_json;
private int m_bufferCount;
private JshStreamController(PrintStream stdOutPrinter, PrintStream stdErrPrinter, int maxBuffer) throws JshException {
m_stdOutPrinter = stdOutPrinter;
m_stdErrPrinter = stdErrPrinter;
StdOutStream = JshStreamFactory.getInstance().makeStream("StdOut", _lock, JshStreamFactory.StreamLevel.TOP_LEVEL);
StdErrStream = JshStreamFactory.getInstance().makeStream("StdErr", _lock, JshStreamFactory.StreamLevel.TOP_LEVEL);
m_maxBuffer = maxBuffer;
GsonBuilder jsonBuilder = new GsonBuilder();
jsonBuilder.registerTypeAdapter(BasicStream.class, new BasicStreamSerializer());
jsonBuilder.registerTypeAdapter(BasicTypeStream.class, new BasicTypeStreamSerializer());
jsonBuilder.registerTypeAdapter(TopLevelStream.class, new TopLevelStreamSerializer());
m_json = jsonBuilder.create();
}
/**
* Increments the count of objects in the buffer.
*
* @param amount The number of objects that have been added to the buffer
*/
void incrementCount(int amount) {
synchronized (_lock) {
m_bufferCount += amount;
if (m_bufferCount >= m_maxBuffer) {
flush();
}
}
}
/**
* Writes all current buffered objects to the initialized stdout and stderr.
*/
public void flush() {
synchronized (_lock) {
m_stdOutPrinter.println(m_json.toJson(StdOutStream.getSubStreams()));
m_stdErrPrinter.println(m_json.toJson(StdErrStream.getSubStreams()));
m_bufferCount = 0;
}
}
}

View File

@ -0,0 +1,54 @@
package edu.wpi.cs.jsh;
import javax.swing.plaf.basic.BasicTreeUI;
/**
* This factory is internal to the jsh api. It is used to create new JshStreams at runtime.
*/
class JshStreamFactory {
public static final JshStreamFactory instance = new JshStreamFactory();
public static JshStreamFactory getInstance() {
return instance;
}
/**
* Creates a new instance of an {@link Stream} implementation with the given name. The type of the stream is
* {@link Stream}
*
* @param name The name of the new stream
* @return The stream instance
*/
public Stream makeStream(String name, Object lock, StreamLevel level) throws JshException {
Stream stream;
switch (level) {
case TOP_LEVEL:
stream = new TopLevelStream(name, lock);
break;
case NORMAL:
stream = new BasicStream(name, lock);
break;
default:
throw new JshException("Error, unrecognized stream level");
}
return stream;
}
/**
* Creates a new instace of an {@link TypeStream} implementation with the given name and base type. The type of
* the stream is the provided type.
*
* @param name The name of the type stream
* @param type The type of the stream. This is necessary because Java lacks reified generics
* @param <T> The type of the stream to be made.
* @return The stream instance.
*/
public <T> TypeStream<T> makeTypeStream(String name, Class<T> type, Object lock) {
return new BasicTypeStream<T>(name, type, lock);
}
public enum StreamLevel {
TOP_LEVEL, NORMAL
}
}

View File

@ -0,0 +1,25 @@
package edu.wpi.cs.jsh;
import java.lang.reflect.Type;
/**
* A named stream is a JshStream that has the concept of having a name and some value. NamedStream implementations should
* actually implement one of {@link Stream} or {@link TypeStream}
*/
public interface NamedStream {
/**
* Gets the name associated with this named stream
*
* @return The stream name
*/
String getName();
/**
* Gets the class associated with this named stream for equality checking
*
* @return The class of the value of this stream
*/
Class<?> getStreamClass();
}

View File

@ -0,0 +1,49 @@
package edu.wpi.cs.jsh;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;
/**
* This is an interface for the idea of a JSH style stream or substream. Streams have the following concepts:
* <ul>
* <li>You can output types on a specific key</li>
* <li>You can create new substreams from this stream</li>
* <li>All types output on a specific key must be the same</li>
* </ul>
* A stream that implements this interface should not directly output values. It outputs the values of the substreams,
* which are either streams or substreams
*/
public interface Stream extends NamedStream {
/**
* Gets a raw type stream of the specified generic type. This output stream can be of any type, but you should not use
* {@link edu.wpi.cs.jsh.Stream} as the type. If you need a new substream, use the {@link edu.wpi.cs.jsh.Stream#getSubStream(String)}
* method instead.
*
* @param <T> The type of the substream. If the stream name is already a stream of a different type, an exception will be thrown
* @param streamName The name of the substream.
* @param streamType The type of T parameter. Due to Java generic limitations, this needs to passed in to be available at runtime
* @return The raw type stream with the given name and type
* @throws JshException Thrown if the stream name is not unique and has conflicting types
*/
<T> TypeStream<T> getTypeStream(String streamName, Class<T> streamType) throws JshException;
/**
* Gets a new Stream that allows for additional substreams. The stream name must be unique, ie this stream does not already
* have any other Streams or TypeStreams that have the same name. Streams do not output directly: if you need to directly output
* a type, use the {@link Stream#getTypeStream(String, Class)} method instead.
*
* @param streamName The name of the substream to create
* @return The substream with the given name
* @throws JshException Thrown if the stream name is not unique and has conflicting types
*/
Stream getSubStream(String streamName) throws JshException;
/**
* Gets all currently created substreams.
*
* @return All substreams
*/
Map<String, NamedStream> getSubStreams();
}

View File

@ -0,0 +1,10 @@
package edu.wpi.cs.jsh;
/**
* Special case of a basic stream that is the top level. The only difference is the deserializers.
*/
public class TopLevelStream extends BasicStream {
public TopLevelStream(String name, Object lock) {
super(name, lock);
}
}

View File

@ -0,0 +1,18 @@
package edu.wpi.cs.jsh;
import com.google.gson.JsonElement;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
/**
* Serializer for the top level stream, which is the same as the rest of the streams except that it does not wrap the stream
* in an array object.
*/
public class TopLevelStreamSerializer implements JsonSerializer<BasicStream> {
@Override
public JsonElement serialize(BasicStream basicStream, Type type, JsonSerializationContext jsonSerializationContext) {
return jsonSerializationContext.serialize(basicStream.getSubStreams());
}
}

View File

@ -0,0 +1,15 @@
package edu.wpi.cs.jsh;
/**
* A type stream is a raw type stream that can output raw types. Type streams use generics to ensure that they are type
* safe. Type streams cannot have substreams, as they put out raw elements.s
*/
public interface TypeStream<T> extends NamedStream {
/**
* Outputs the given set of values on this stream.
* @param values
*/
void output(T... values);
}

View File

@ -1,15 +0,0 @@
import org.junit.Test;
import static org.junit.Assert.*;
/*
* This Java source file was auto generated by running 'gradle init --type java-library'
* by 'fred' at '11/6/14 4:30 PM' with Gradle 2.1
*
* @author fred, @date 11/6/14 4:30 PM
*/
public class LibraryTest {
@Test public void testSomeLibraryMethod() {
Library classUnderTest = new Library();
assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod());
}
}

View File

@ -0,0 +1,95 @@
package edu.wpi.cs.jsh;
import com.google.gson.*;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Created by fred on 11/10/14.
*/
public class BasicStreamTest {
@Before
public void setup() throws JshException {
JshStreamController.reset();
JshStreamController.initialize();
}
@Test
public void testEmptySubStream() throws JshException {
Stream superStream = JshStreamController.getInstance().StdOutStream;
superStream.getSubStream("TestSubStream");
Gson json = new GsonBuilder()
.registerTypeAdapter(BasicTypeStream.class, new BasicTypeStreamSerializer())
.registerTypeAdapter(BasicStream.class, new BasicStreamSerializer())
.registerTypeAdapter(TopLevelStream.class, new TopLevelStreamSerializer())
.create();
JsonElement actual = json.toJsonTree(superStream);
JsonObject expected = new JsonObject();
JsonArray array = new JsonArray();
array.add(new JsonObject());
expected.add("TestSubStream", array);
assertEquals(expected, actual);
}
@Test
public void testIntSubStream() throws JshException {
Stream superStream = JshStreamController.getInstance().StdOutStream;
TypeStream<Integer> subStream = superStream.getTypeStream("TestSubStream", Integer.class);
subStream.output(1, 2, 3);
Gson json = new GsonBuilder()
.registerTypeAdapter(BasicTypeStream.class, new BasicTypeStreamSerializer())
.registerTypeAdapter(BasicStream.class, new BasicStreamSerializer())
.registerTypeAdapter(TopLevelStream.class, new TopLevelStreamSerializer())
.create();
JsonElement actual = json.toJsonTree(superStream);
JsonObject expected = new JsonObject();
JsonArray array = new JsonArray();
array.add(new JsonPrimitive(1));
array.add(new JsonPrimitive(2));
array.add(new JsonPrimitive(3));
expected.add("TestSubStream", array);
assertEquals(expected, actual);
}
@Test
public void testStringSubStream() throws JshException {
Stream superStream = JshStreamController.getInstance().StdOutStream;
TypeStream<String> subStream = superStream.getTypeStream("TestSubStream", String.class);
subStream.output("First", "Second", "Third");
Gson json = new GsonBuilder()
.registerTypeAdapter(BasicTypeStream.class, new BasicTypeStreamSerializer())
.registerTypeAdapter(BasicStream.class, new BasicStreamSerializer())
.registerTypeAdapter(TopLevelStream.class, new TopLevelStreamSerializer())
.create();
JsonElement actual = json.toJsonTree(superStream);
JsonObject expected = new JsonObject();
JsonArray array = new JsonArray();
array.add(new JsonPrimitive("First"));
array.add(new JsonPrimitive("Second"));
array.add(new JsonPrimitive("Third"));
expected.add("TestSubStream", array);
assertEquals(expected, actual);
}
@Test
public void testSubSubStream() throws JshException {
Stream superStream = JshStreamController.getInstance().StdOutStream;
superStream.getSubStream("SubStream").getTypeStream("SubSubStream", Integer.class);
Gson json = new GsonBuilder()
.registerTypeAdapter(BasicTypeStream.class, new BasicTypeStreamSerializer())
.registerTypeAdapter(BasicStream.class, new BasicStreamSerializer())
.registerTypeAdapter(TopLevelStream.class, new TopLevelStreamSerializer())
.create();
JsonElement actual = json.toJsonTree(superStream);
JsonObject expected = new JsonObject();
JsonArray array = new JsonArray();
JsonObject subSubObject = new JsonObject();
subSubObject.add("SubSubStream", new JsonArray());
array.add(subSubObject);
expected.add("SubStream", array);
assertEquals(expected, actual);
}
}

View File

@ -0,0 +1,76 @@
package edu.wpi.cs.jsh;
import com.google.gson.*;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Testing basic type stream serialization and output
*/
public class BasicTypeStreamTest {
@Before
public void setup() {
try {
JshStreamController.reset();
JshStreamController.initialize();
} catch (JshException e) {
e.printStackTrace();
}
}
@Test
public void testIntStream() throws JshException {
TypeStream<Integer> intStream = JshStreamController.getInstance().StdOutStream.getTypeStream("IntStream", Integer.class);
intStream.output(1, 2, 3);
Gson json = new GsonBuilder().registerTypeAdapter(BasicTypeStream.class, new BasicTypeStreamSerializer()).create();
JsonElement output = json.toJsonTree(intStream);
JsonArray expected = new JsonArray();
expected.add(new JsonPrimitive(1));
expected.add(new JsonPrimitive(2));
expected.add(new JsonPrimitive(3));
assertEquals(output, expected);
}
@Test
public void testStringStream() throws JshException {
TypeStream<String> intStream = JshStreamController.getInstance().StdOutStream.getTypeStream("IntStream", String.class);
intStream.output("First", "Second", "Third");
Gson json = new GsonBuilder().registerTypeAdapter(BasicTypeStream.class, new BasicTypeStreamSerializer()).create();
JsonElement output = json.toJsonTree(intStream);
JsonArray expected = new JsonArray();
expected.add(new JsonPrimitive("First"));
expected.add(new JsonPrimitive("Second"));
expected.add(new JsonPrimitive("Third"));
assertEquals(output, expected);
}
@Test
public void testObjectStream() throws JshException {
TypeStream<TestClass> intStream = JshStreamController.getInstance().StdOutStream.getTypeStream("IntStream", TestClass.class);
TestClass firstElement = new TestClass(1 , 2, "three");
TestClass secondElement = new TestClass(4, 5, "six");
TestClass thirdElement = new TestClass(7, 8, "nine");
intStream.output(firstElement, secondElement, thirdElement);
Gson json = new GsonBuilder().registerTypeAdapter(BasicTypeStream.class, new BasicTypeStreamSerializer()).create();
JsonElement output = json.toJsonTree(intStream);
JsonArray expected = new JsonArray();
expected.add(json.toJsonTree(firstElement));
expected.add(json.toJsonTree(secondElement));
expected.add(json.toJsonTree(thirdElement));
assertEquals(output, expected);
}
private class TestClass {
int value1;
int value2;
String value3;
public TestClass(int value1, int value2, String value3) {
this.value1 = value1;
this.value2 = value2;
this.value3 = value3;
}
}
}