Skip to content
Snippets Groups Projects
Commit 5fce6747 authored by Håkon Gylterud's avatar Håkon Gylterud
Browse files

No git history available for InChat.

parents
No related branches found
No related tags found
No related merge requests found
package inf226.storage;
import java.util.UUID;
public class Stored<T> {
public final T value;
public final UUID identity;
public final UUID version;
/**
* The default constructor for creating a new Stored value.
**/
public Stored(T value) {
this.value = value;
this.identity = UUID.randomUUID();
this.version = UUID.randomUUID();
}
/**
* Construct a new version of this stored object.
**/
public Stored<T> newVersion(T newValue) {
return new Stored<T>(newValue , identity, UUID.randomUUID());
}
/**
* The constructor for recreating a stored object
* from a serialised version.
**/
public Stored(T value, UUID identity, UUID version) {
this.value = value;
this.identity = identity;
this.version = version;
}
@Override
public boolean equals(Object other) {
if (other == null)
return false;
if (getClass() != other.getClass())
return false;
@SuppressWarnings("unchecked")
final Stored<T> stored_other = (Stored<T>) other;
return this.identity.equals(stored_other.identity)
&& this.version.equals(stored_other.version)
&& this.value.equals(stored_other.value);
}
}
package inf226.storage;
import inf226.storage.Stored;
public class UpdatedException extends Exception {
private static final long serialVersionUID = 8516366302597379968L;
public final Stored newObject;
public UpdatedException(Stored newObject) {
super("Object was updated");
this.newObject = newObject;
}
@Override
public Throwable fillInStackTrace() {
return this; // We do not want stack traces for these exceptions.
}
}
package inf226.util;
import java.util.function.Consumer;
import java.util.function.Function;
public class Either<A,B> {
private final boolean isLeft;
private final A left;
private final B right;
private Either(A leftValue, B rightValue, boolean isLeft) {
this.left = leftValue;
this.right = rightValue;
this.isLeft = isLeft;
}
public static<U,V> Either<U,V> left(U value) {
return new Either<U,V>(value, null, true);
}
public static<U,V> Either<U,V> right(V value) {
return new Either<U,V>(null, value, false);
}
public void branch(Consumer<A> leftBranch, Consumer<B> rightBranch) {
if (isLeft)
leftBranch.accept(left);
else
rightBranch.accept(right);
}
public<C> C cases(Function<A,C> leftCase, Function<B,C> rightCase) {
if (isLeft)
return leftCase.apply(left);
else
return rightCase.apply(right);
}
}
package inf226.util;
import java.util.function.Consumer;
import java.util.function.Function;
public class Maybe<T> {
private final T value;
public Maybe(T value) {
this.value = value;
}
public T get() throws NothingException {
if(value == null)
throw new NothingException();
else
return value;
}
public static<U> Maybe<U> nothing() {
return new Maybe<U>(null);
}
public static<U> Maybe<U> just(U value) {
return new Maybe<U>(value);
}
@Override
public final boolean equals(Object other) {
if (other == null)
return false;
if (getClass() != other.getClass())
return false;
@SuppressWarnings("unchecked")
final Maybe<Object> maybe_other = (Maybe<Object>) other;
if(maybe_other.value == null && value == null)
return true;
if(value == null)
return false;
return value.equals(maybe_other.value);
}
public void forEach(Consumer<T> c) {
if (value == null)
return ;
else
c.accept(value);
}
public<U> Maybe<U> map(Function<T,U> f) {
if (value == null)
return nothing();
else
return just(f.apply(value));
}
public<U> Maybe<U> bind(Function<T,Maybe<U>> f) {
if (value == null)
return nothing();
else
return f.apply(value);
}
public T defaultValue(T e) {
if (value == null)
return e;
else
return value;
}
public boolean isNothing() {
return (value == null);
}
public Maybe<T> supremum(Maybe<T> other) {
if (value == null)
return other;
else
return this;
}
public static<U> Builder<U> builder() { return new Builder<U>() ; }
public static class Builder<U> implements Consumer<U> {
private Maybe<U> value;
public Builder() { value = nothing(); }
@Override
public void accept(U value) { (new Maybe<U>(value)).forEach(v -> this.value = new Maybe<U>(v)); }
public Maybe<U> getMaybe(){ return value ;};
}
public static class NothingException extends Exception {
private static final long serialVersionUID = 8141663032597379968L;
public NothingException() {
super("Unexpected Maybe.nothing()");
}
@Override
public Throwable fillInStackTrace() {
return this;
}
}
}
package inf226.util;
import java.util.function.Consumer;
/**
* Store a mutable variable as a consumer.
* This class is a wrapper for avoiding some limitations
* of Java's λ-abstractions.
**/
public class Mutable<A> implements Consumer<A>{
private A value;
public Mutable(A value) {
this.value = value;
}
public static<U> Mutable<U> init(U value) {
return new Mutable<U>(value);
}
@Override
public void accept(A value) {
this.value = value;
}
public A get() {
return value;
}
}
package inf226.util;
public final class Pair<A,B> {
public final A first;
public final B second;
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
public static<U,V> Pair<U,V> pair(U first, V second) {
return new Pair<U,V>(first, second);
}
@Override
public final boolean equals(Object other) {
if (other == null)
return false;
if (getClass() != other.getClass())
return false;
@SuppressWarnings("unchecked")
final Pair<Object,Object> pair_other = (Pair<Object,Object>) other;
return this.first.equals(pair_other.first)
&& this.second.equals(pair_other.second);
}
}
package inf226.util;
import inf226.util.immutable.List;
import java.lang.Throwable;
import java.util.function.Function;
import inf226.storage.*;
public class Util {
public static<E extends Throwable> void throwMaybe(Maybe<E> exception) throws E {
try { throw exception.get(); }
catch (Maybe.NothingException e) { /* Intensionally left blank */ }
}
public static<A,B> Maybe<B> lookup(List<Pair<A,B>> list, A key) {
final Maybe.Builder<B> result
= new Maybe.Builder<B>();
list.forEach(pair -> {
if(pair.first.equals(key))
result.accept(pair.second);
});
return result.getMaybe();
}
public static<A,Q, E extends Exception>
Stored<A> updateSingle(Stored<A> stored,
Storage<A,E> storage,
Function<Stored<A>,A> update)
throws E, DeletedException {
boolean updated = true;
while(true) {
try {
return storage.update(stored,update.apply(stored));
} catch (UpdatedException e) {
stored = (Stored<A>)e.newObject;
}
}
}
public static<A,Q, E extends Exception> void deleteSingle(Stored<A> stored, Storage<A,E> storage)
throws E {
while(true) {
try {
storage.delete(stored);
} catch (UpdatedException e) {
stored = (Stored<A>)e.newObject;
} catch (DeletedException e) {
return;
}
}
}
}
package inf226.util.immutable;
import inf226.util.Maybe;
import inf226.util.Mutable;
import java.util.function.Function;
import java.util.function.BiFunction;
import java.util.function.Consumer;
public final class List<T> {
private final Maybe<ListItem<T> > items;
public final Maybe<T> last;
public final int length;
private List() {
this.items = Maybe.nothing();
this.last = Maybe.nothing();
this.length = 0;
}
private List(T head, List<T> tail) {
this.items = new Maybe<ListItem<T>>(new ListItem<T>(head, tail));
/* Construct a reference to the last element of
the list. */
T new_last;
try {
new_last = tail.last.get();
} catch (Maybe.NothingException e) {
new_last = head;
}
this.last = new Maybe<T>(new_last);
this.length = tail.length + 1;
}
public static<U> List<U> empty() {
return new List<U>();
}
public static<U> List<U> cons(U head, List<U> tail) {
return new List<U>(head,tail);
}
public static<U> List<U> singleton(U head) {
return new List<U>(head,empty());
}
public Maybe<T> head() {
try {
return new Maybe<T>(items.get().head);
} catch (Maybe.NothingException e) {
return Maybe.nothing();
}
}
public Maybe< List<T> > tail() {
try {
return new Maybe<List<T> >(items.get().tail);
} catch (Maybe.NothingException e) {
return Maybe.nothing();
}
}
public List<T> add(T element) {
return cons(element, this);
}
public<U> List<U> map(Function<T,U> f) {
List<U> result = empty();
try {
for(List<T> l = this.reverse(); ; l = l.tail().get()) {
result = cons(f.apply(l.head().get()), result);
}
} catch (Maybe.NothingException e) {
// No more elements
}
return result;
}
public<B,C> List<C> zipWith(List<B> other, BiFunction<T,B,C> f) {
Builder<C> result = builder();
try {
List<T> l0 = this.reverse();
List<B> l1 = other.reverse();
while(true) {
result.accept(f.apply(l0.items.get().head, l1.items.get().head));
l0 = l0.items.get().tail;
l1 = l1.items.get().tail;
}
} catch (Maybe.NothingException e) {
// No more elements
}
return result.getList();
}
@Override
public final boolean equals(Object other) {
if (other == null)
return false;
if (getClass() != other.getClass())
return false;
@SuppressWarnings("unchecked")
final List<Object> list_other = (List<Object>) other;
final Mutable<Boolean> equal = new Mutable<Boolean>(length == list_other.length);
List<Boolean> equalList = zipWith(list_other, (a, b) -> a.equals(b));
equalList.forEach(e -> { equal.accept(equal.get() && e);});
return equal.get();
}
public void forEach(Consumer<T> c) {
sequenceConsumer(c).accept(this);
}
public static<U> Consumer<List<U>> sequenceConsumer(Consumer<U> c) {
return new Consumer<List<U>>(){
@Override
public void accept(List<U> l) {
try {
for(ListItem<U> e = l.items.get(); ; e = e.tail.items.get()) {
c.accept(e.head);
}
} catch (Maybe.NothingException e) {
// No more elements
}
} };
}
public static class Builder<U> implements Consumer<U> {
private List<U> list;
public Builder() { list = empty(); }
@Override
public synchronized void accept(U element) { list = cons(element, list) ; }
public List<U> getList() { return list ; }
}
public List<T> reverse() {
List<T> result = empty();
try {
for(ListItem<T> e = this.items.get(); ; e = e.tail.items.get()) {
result = cons(e.head, result);
}
} catch (Maybe.NothingException e) {
// No more elements
}
return result;
}
public static<U> Builder<U> builder(){return new Builder<U>();}
private static class ListItem<T> {
public final T head;
public final List<T> tail;
ListItem(T head, List<T> tail) {
this.head = head;
this.tail = tail;
}
}
}
package inf226.inchat;
import org.junit.jupiter.api.Test;
import inf226.storage.*;
import inf226.util.*;
import java.util.UUID;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.DriverManager;
public class InchatTest{
@Test
void chatSetup() throws Maybe.NothingException,SQLException {
UUID testID = UUID.randomUUID();
System.err.println("Running test:" + testID);
final String path = "test" + testID + ".db";
final String dburl = "jdbc:sqlite:" + path;
final Connection connection = DriverManager.getConnection(dburl);
connection.createStatement().executeUpdate("PRAGMA foreign_keys = ON");
UserStorage userStore
= new UserStorage(connection);
EventStorage eventStore
= new EventStorage(connection);
ChannelStorage channelStore
= new ChannelStorage(connection,eventStore);
AccountStorage accountStore
= new AccountStorage(connection,userStore,channelStore);
SessionStorage sessionStore
= new SessionStorage(connection,accountStore);
InChat inchat = new InChat(userStore,channelStore,accountStore,sessionStore);
Stored<Session> aliceSession = inchat.register("Alice","badpassword").get();
inchat.register("Bob","worse").get();
Stored<Session> bobSession = inchat.login("Bob","worse").get();
Stored<Channel> channel = inchat.createChannel(aliceSession.value.account,"Awesome").get();
inchat.postMessage(aliceSession.value.account,channel, "Test message.").get();
inchat.joinChannel(bobSession.value.account,channel.identity).get();
}
}
style.css 0 → 100644
body{
margin: 0 auto;
font-family: Georgia, Palatino, serif;
color: #040404;
line-height: 1;
max-width: 50em;
padding: 30px;
text-align: justify;
border-radius: 1em;
}
.thread-head {
display: grid;
margin: 0;
padding: 0.1em 0.1em 0.1em;
grid-template-columns: 20% auto;
grid-template-areas:
"topic topic"
"starter date";
}
.forum-head {
display: grid;
margin: 0;
grid-template-columns: 20% auto;
grid-template-areas:
"topic topic"
"symbol description";
}
.forum {
display: grid;
grid-gap: 1em;
grid-template-columns: 50% 50%;
grid-template-areas:
"header header";
}
.main {
max-width: 50em;
display: grid;
margin: 0;
grid-template-columns: 10em auto 10em;
grid-template-areas:
"chanlist channel chanmenu";
}
.channel {
grid-area: channel;
padding-right: 0.5em;
}
.chanlist {
grid-area: chanlist;
list-style-type: square;
max-width: 10em;
}
.chanmenu {
grid-area: chanmenu;
max-width: 10em;
}
header {
grid-area: header;
}
.starter{
grid-area: starter;
margin: 0 0 0 0;
padding: 0.1em 0.1em 1em 0.1em;
background-color: #e0f0ff;
border: 0.1em solid #88bbff;
}
.date{
text-align: right;
grid-area: date;
margin: 0 0 0 0;
padding: 0.1em 0.1em 1em 0.1em;
background-color: #e0f0ff;
border: 0.1em solid #88bbff;
}
.topic {
grid-area: topic;
margin: 0;
padding: 0.8em 0.8em 0.5em 0.8em;
border-top-left-radius: 1em;
border-top-right-radius: 1em;
background-color: #88bbff;
color: #040404;
}
.actionbar {
padding: 0.4em 0 0.4em 0;
margin-bottom: 0.4em;
}
.action {
border: 0.1em solid #88bbff;
margin: auto;
padding: 0.3em;
font-size: 1em;
}
.register {
padding: 2em;
margin: auto;
width: 50%;
}
.login {
padding: 2em;
margin: auto;
width: 50%;
}
.post {
margin: auto;
width: 100%;
}
.messagebox {
grid-area: text;
width: 100%;
}
.entry {
display: grid;
margin: 0;
padding: 0.1em 0.1em 0.1em;
grid-template-columns: 20% auto;
grid-template-areas:
"user text"
"footer footer";
background-color: #ffffff;
border: 0.1em solid #88bbff;
}
.controls {
grid-area: footer;
float: right;
padding: 1em 0.1em 1em 0.1em;
}
.messagecontrols {
grid-area: footer;
float: right;
display: grid;
padding: 1em 0.1em 1em 0.1em;
grid-template-columns: 20% auto;
grid-template-areas:
"delete edit"
}
.user{
grid-area: user;
margin: 0 0 0 0;
padding: 0.1em 0.1em 1em 0.1em;
}
.text{
grid-area: text;
margin: 0 0 0 0;
padding: 0.6em 0.6em 1em 0.6em;
background-color: #ffffff;
}
.blob {
margin: 2em 0em 2em 0em;
height: 10em;
}
.author:before {
content: "—";
}
.author {
color: #505050;
margin-left: 1em;
}
h2 {
font-size: 2.3em;
font-weight: normal;
}
p {
margin-top: 0;
margin-bottom: 1em;
}
a {
color: #0066CC;
margin: 0;
padding: 0;
vertical-align: baseline;
text-decoration: none;
}
a:hover {
text-decoration: none;
color: #CC0000;
}
a:visited {
color: #6633FF;
}
a:after {
content: "";
font-size: small;
}
ul, ol {
padding: 0;
margin: 0 24px;
}
li {
line-height: 24px;
}
li ul, li ul {
margin-left: 48px;
}
p, ul, ol {
font-size: 16px;
line-height: 24px;
}
p {
max-width: 40em;
}
ul,ol {
max-width: 36em;
}
pre {
padding: 0px 24px;
max-width: 40em;
white-space: pre-wrap;
}
code {
font-family: Consolas, Monaco, Andale Mono, monospace;
line-height: 1.5;
font-size: 13px;
}
aside {
display: block;
float: right;
width: 390px;
}
blockquote {
border-left:.5em solid #eee;
padding: 0 2em;
margin-left:0;
max-width: 476px;
}
blockquote cite {
font-size:14px;
line-height:20px;
color:#bfbfbf;
}
blockquote cite:before {
content: '\2014 \00A0';
}
blockquote p {
color: #666;
max-width: 36em;
}
hr {
width: 80%;
text-align: left;
margin: 0 auto 0 0;
color: #999;
}
figure, img {
max-width: 36em;
}
input[type=submit] {
border: 0.1em solid #88bbff;
margin-right: 0.5em;
}
input[type=submit]:hover {
background-color: #88bbff;
margin-right: 0.5em;
}
<!DOCTYPE html>
<html lang="en-GB">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<style type="text/css">code{white-space: pre;}</style>
<link rel="stylesheet" href="style.css">
</head>
<body>
<section>
<header class="thread-head">
<h2 class="topic">The fooobar topic thread</h2>
<div class="starter">Joe</div>
<div class="date">2019–09–20</div>
</header>
<div class="entry">
<div class="user">Joe</div>
<div class="text"><p>I started this thread in order to test the
layout of this web page.</p>
<p>I expect to writ a lot here later – especially about lorem ipsum.
But for the moment I will content myself with a few lines.</p>
</div>
</div>
</section>
</body>
</html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment