Compare commits

...
Sign in to create a new pull request.

19 commits
main ... dev

Author SHA1 Message Date
jeffcheasey88
b77c6d120e DependencyInjector -> Add method to provide a Supplier for a Type 2025-05-02 17:36:09 +02:00
jeffcheasey88
dfeb611532 ResponseMapper (base) 2025-05-02 17:02:36 +02:00
jeffcheasey88
bbb842887b forgot sysout 2025-05-02 16:48:45 +02:00
jeffcheasey88
c2eed8b2fe Update Parser Dependency 2025-05-02 16:48:07 +02:00
jeffcheasey88
d75e736ba3 RouteInterceptor -> All interceptors are now linked with Routes 2025-05-02 11:04:00 +02:00
jeffcheasey88
f47551fc6f RouteInterceptor -> Add possibility to associate an interceptor with specific Route(s) 2025-05-01 20:20:21 +02:00
jeffcheasey88
9cedf6898f RouteBinder -> When declaring a Route, developper can now auto-bind other types than [Matcher, Context, HttpReader, HttpWriter]. Currently, the value will be bind with an interceptor. 2025-05-01 19:28:08 +02:00
jeffcheasey88
cdafc9cbaf Add constructor to JsonArray & JsonMap for bettr developer experience 2025-05-01 17:32:20 +02:00
jeffcheasey88
12f2561921 StrictReader (will be optimise later) -> use a 1 byte buffer to read data, so it can read bytes when needed without skiping any 2025-04-11 14:39:33 +02:00
jeffcheasey88
2c135c2e26 Remove unnecessary method 2025-02-01 16:24:46 +01:00
jeffcheasey88
f2ecb3a1eb Remove JWT dependency in User 2025-02-01 16:17:36 +01:00
jeffcheasey88
012978d759 Refractor Locker -> fix one concurent exception on HashMap + clean the code 2025-01-31 11:38:41 +01:00
jeffcheasey88
a79876b71c Fix Java's Method access + Always response 401 when no token -> even when not neccessary 2025-01-26 11:38:21 +01:00
jeffcheasey88
7e4cc9f425 Refractor Authenticator -> was visibly not finished previously 2025-01-24 23:15:15 +01:00
jeffcheasey88
8a6b744519 Move JWT system out of router & add possibilité to custom internal Authenticator 2024-10-14 16:10:45 +02:00
jeffcheasey88
9193e0d9d3 Fix callup stacktrace when inner call on RegisterPackages 2024-09-23 18:03:22 +02:00
jeffcheasey88
9f11825f97 RegisterPackages -> ignore other files in compiled runtime directory 2024-09-23 03:33:05 +02:00
jeffcheasey88
c06cc46787 Refractor Dependency Injector -> Add public methods for developper's usage, optimise searching by using correctly the cache, add toString() method for debugs 2024-09-20 23:38:20 +02:00
jeffcheasey88
2cf4363e25 Add Constroller system; a class that contains 0-N methods with Route's annotation; can have Matcher, Context, Reader or Write, or none of them 2024-09-17 13:00:52 +02:00
22 changed files with 990 additions and 258 deletions

Binary file not shown.

View file

@ -2,7 +2,7 @@ package dev.peerat.framework;
import java.net.Socket;
import org.jose4j.jwt.consumer.InvalidJwtException;
import dev.peerat.framework.auth.AuthException;
public class Client<U extends User> extends Thread{
@ -28,38 +28,35 @@ public class Client<U extends User> extends Thread{
router.exec(context, reader, writer);
writer.flush();
writer.close();
}catch(InvalidJwtException e){
this.router.getExceptionLogger().setValue(e);
}catch(AuthException e){
this.router.getExceptionLogger().pushValue(e);
}catch(Throwable e){
this.router.getExceptionLogger().setValue(e);
this.router.getExceptionLogger().pushValue(e);
if(context != null && context.getResponseCode() == 0){
try{
context.response(500);
writer.flush();
writer.close();
}catch(Exception ex){
this.router.getExceptionLogger().setValue(ex);
this.router.getExceptionLogger().pushValue(ex);
}
}
}
if(context != null) router.getLogger().setValue(context);
if(context != null) router.getLogger().pushValue(context);
}
private User isLogin(RequestType type, HttpReader reader) throws InvalidJwtException{
String auth = reader.getHeader("Authorization");
if(auth == null) return null;
auth = auth.substring(7);
private User isLogin(RequestType type, HttpReader reader) throws AuthException{
try{
return this.router.getUser(auth);
}catch(InvalidJwtException e){
return this.router.getUser(type, reader);
}catch(Exception e){
try{
writer.response(401, this.router.getDefaultHeaders(type));
writer.flush();
writer.close();
}catch(Exception ex){
this.router.getExceptionLogger().setValue(ex);
this.router.getExceptionLogger().pushValue(ex);
}
throw e;
throw new AuthException(e);
}
}
}

View file

@ -1,21 +1,26 @@
package dev.peerat.framework;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Supplier;
public class DependencyInjector{
private Map<String, Object> map;
private List<BiFunction<Constructor<?>, Parameter, Object>> builders;
private Map<Class<?>, Supplier<?>> suppliers;
private List<BiFunction<Injection, Class<?>, Object>> builders;
private Object[] injections;
private Map<Class<?>, Object> cache;
public DependencyInjector(){
this.map = new HashMap<>();
this.suppliers = new HashMap<>();
this.builders = new ArrayList<>();
}
@ -24,14 +29,20 @@ public class DependencyInjector{
return this;
}
public DependencyInjector of(BiFunction<Constructor<?>, Parameter, Object> builder){
public DependencyInjector of(BiFunction<Injection, Class<?>, Object> builder){
this.builders.add(builder);
return this;
}
public DependencyInjector of(Class<?> type, Supplier<?> supplier){
this.suppliers.put(type, supplier);
return this;
}
public DependencyInjector of(Object... injections){
if(this.injections == null){
this.injections = injections;
this.cache = new HashMap<>();
return this;
}
Object[] copy = new Object[this.injections.length+injections.length];
@ -40,31 +51,57 @@ public class DependencyInjector{
this.injections = copy;
return this;
}
Object applyDependency(Constructor<?> constructor, Parameter parameter, Map<Class<?>, Object> cache){
Injection annotation = parameter.getAnnotation(Injection.class);
public Object[] apply(Executable executable) throws Exception{
Parameter[] parameters = executable.getParameters();
Object[] injections = new Object[parameters.length];
for(int i = 0; i < parameters.length; i++) injections[i] = find(parameters[i].getAnnotation(Injection.class), parameters[i].getType());
return injections;
}
public Object apply(Field field) throws Exception{
return find(field.getAnnotation(Injection.class), field.getType());
}
public Object find(Injection annotation, Class<?> type){
if(annotation != null){
Object result = this.map.get(annotation.value());
if(result == null) throw new IllegalArgumentException("No depdency named "+annotation.value()+" ("+this.map.keySet()+") in constructor "+constructor);
if(result == null){
Supplier<?> supplier = this.suppliers.get(type);
if(supplier == null) throw new IllegalArgumentException("No dependency named "+annotation.value()+" ("+this.map.keySet()+")");
result = supplier.get();
this.map.put(annotation.value(), result);
}
return result;
}
for(BiFunction<Constructor<?>, Parameter, Object> function : builders){
Object result = function.apply(constructor, parameter);
for(BiFunction<Injection, Class<?>, Object> function : builders){
Object result = function.apply(annotation, type);
if(result != null) return result;
}
if(this.injections == null) return null;
Class<?> type = parameter.getType();
Object result = null;
if(this.injections == null){
Supplier<?> supplier = this.suppliers.get(type);
if(supplier == null) throw new IllegalArgumentException("No dependency for type "+type);
return supplier.get();
}
Object result = this.cache.get(type);
if(result != null) return result;
for(Object injection : injections){
if(type.isAssignableFrom(injection.getClass())){
if(cache.containsKey(type)){
throw new IllegalArgumentException("Double dependency for type "+type+", can be in constructor "+constructor);
}else{
cache.put(type, injection);
result = injection;
}
if(cache.containsKey(type)) throw new IllegalArgumentException("Double dependency for type "+type);
cache.put(type, injection);
result = injection;
}
}
if(result == null){
Supplier<?> supplier = this.suppliers.get(type);
if(supplier == null) throw new IllegalArgumentException("No dependency for type "+type);
return supplier.get();
}
return result;
}
@Override
public String toString(){
return "([DependencyInjector] map="+map+",buildersCount="+builders.size()+",injections="+injections+",cache="+cache+")";
}
}

View file

@ -1,9 +1,7 @@
package dev.peerat.framework;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
@ -23,14 +21,14 @@ public class HttpReader{
private Socket socket;
private InputStream in;
private BufferedReader reader;
private StrictReader reader;
private Map<String, String> headers;
public HttpReader(Socket socket) throws Exception{
this.socket = socket;
this.in = socket.getInputStream();
this.reader = new BufferedReader(new InputStreamReader(in));
this.reader = new StrictReader(this.in);
this.headers = new HashMap<>();
}
@ -66,7 +64,7 @@ public class HttpReader{
}
public int read(char[] buffer) throws IOException{
return this.reader.read(buffer);
return this.reader.read(buffer, 0, buffer.length);
}
public String readLine() throws IOException{
@ -133,4 +131,39 @@ Content-Type: text/javascript
return list;
}
public byte[] readMultipartFile() throws Exception{
int length = Integer.parseInt(getHeader("content-length"));
int headerLength = readLine().length()+2;
length-=headerLength;
for(int i = 0; i < 2; i++){
length-=(readLine().length()+2);
}
byte[] ptn = new byte[2];
read(ptn);
length-=2;
if(ptn[0] == 10){
read(new byte[1]);
length-=1;
}
byte[] file = new byte[0];
while(length > 0){
byte[] buffer = new byte[length];
int read = read(buffer);
if(read < 0) throw new IndexOutOfBoundsException("read -1 on HttpReader");
length-=read;
byte[] copy = new byte[file.length+read];
System.arraycopy(file, 0, copy, 0, file.length);
System.arraycopy(buffer, 0, copy, file.length, read);
file = copy;
}
byte[] cleaned = new byte[file.length-(headerLength+4)];
System.arraycopy(file, 0, cleaned, 0, cleaned.length);
return cleaned;
}
}

View file

@ -1,9 +1,8 @@
package dev.peerat.framework;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.Consumer;
import java.util.function.Supplier;
@ -13,7 +12,7 @@ public class Locker<V>{
private Map<Key, BlockingQueue<V>> map;
public Locker(){
this.map = new HashMap<>();
this.map = new ConcurrentHashMap<>();
}
public void init(Key key){
@ -24,25 +23,20 @@ public class Locker<V>{
this.map.remove(key);
}
private BlockingQueue<V> get(Key key){
return this.map.get(key);
}
public void setValue(V value){
for(Entry<Key, BlockingQueue<V>> entry : this.map.entrySet()){
entry.getValue().add(value);
this.unlock(entry.getKey());
public void pushValue(V value){
for(BlockingQueue<V> queue : this.map.values()){
queue.add(value);
unlock(queue);
}
}
public V getValue(Key key){
BlockingQueue<V> queue = get(key);
if(queue.isEmpty()) return null;
public V getValue(Key key) throws InterruptedException{
BlockingQueue<V> queue = this.map.get(key);
lock(queue);
return queue.poll();
}
public void lock(Key key) throws InterruptedException{
BlockingQueue<V> queue = get(key);
private void lock(BlockingQueue<V> queue) throws InterruptedException{
if(queue.isEmpty()){
synchronized(queue){
queue.wait();
@ -50,8 +44,7 @@ public class Locker<V>{
}
}
public void unlock(Key key){
BlockingQueue<V> queue = get(key);
private void unlock(BlockingQueue<V> queue){
synchronized(queue){
queue.notify();
}
@ -61,46 +54,40 @@ public class Locker<V>{
Key key = new Key();
init(key);
try {
while(true){
lock(key);
V value = getValue(key);
action.accept(value);
}
while(true) action.accept(getValue(key));
}catch(Exception e){}
remove(key);
}
public void listen(Supplier<Boolean> condition,Consumer<V> action){
public void listen(Consumer<V> action, Consumer<Exception> onClose){
Key key = new Key();
init(key);
try {
while(condition.get()){
lock(key);
V value = getValue(key);
action.accept(value);
}
while(true) action.accept(getValue(key));
}catch(Exception e){
onClose.accept(e);
}
remove(key);
}
public void listen(Supplier<Boolean> condition, Consumer<V> action){
Key key = new Key();
init(key);
try {
while(condition.get()) action.accept(getValue(key));
}catch(Exception e){}
remove(key);
}
public Thread listenAsync(Consumer<V> action){
Thread thread = new Thread(new Runnable(){
public void run(){
listen(action);
}
});
thread.start();
return thread;
}
public Thread listenAsync(Supplier<Boolean> condition,Consumer<V> action){
Thread thread = new Thread(new Runnable(){
public void run(){
listen(condition, action);
}
});
thread.start();
return thread;
public void listen(Supplier<Boolean> condition, Consumer<V> action, Consumer<Exception> onClose){
Key key = new Key();
init(key);
try {
while(condition.get()) action.accept(getValue(key));
}catch(Exception e){
onClose.accept(e);
}
remove(key);
}
public static class Key{ public Key(){} }

View file

@ -0,0 +1,7 @@
package dev.peerat.framework;
public interface ResponseMapper{
void map(Context context, HttpReader reader, HttpWriter writer, Object result) throws Exception;
}

View file

@ -0,0 +1,72 @@
package dev.peerat.framework;
import java.lang.reflect.Method;
import java.util.regex.Matcher;
public class RouteBinder{
//TODO interceptor -------> bind to a specific route, no all routes ? or both. Could be interesting for somes cases !
private Class<?>[] types;
private int[] bindMethod;
private int parameterCount;
public RouteBinder(){}
public void bind(Class<?>... types){
if(this.types == null){
this.types = types;
this.parameterCount = this.types.length;
return;
}
Class<?>[] copy = new Class[this.types.length+types.length];
System.arraycopy(this.types, 0, copy, 0, this.types.length);
System.arraycopy(types, 0, copy, this.types.length, types.length);
this.types = copy;
this.parameterCount = this.types.length;
}
public void bindIndexes(Method method){
Class<?>[] parameters = method.getParameterTypes();
this.bindMethod = new int[4+(types == null ? 0 : types.length)];
this.bindMethod[0] = findIndex(parameters, Matcher.class);
this.bindMethod[1] = findIndex(parameters, Context.class);
this.bindMethod[2] = findIndex(parameters, HttpReader.class);
this.bindMethod[3] = findIndex(parameters, HttpWriter.class);
if(this.types == null) return;
for(int i = 0; i < this.types.length; i++){
this.bindMethod[4+i] = findIndex(parameters, this.types[i]);
}
this.types = null;
}
public int getParameterCount(){
return this.parameterCount;
}
public Object[] bindMethod(Method method, Matcher matcher, Context context, HttpReader reader, HttpWriter writer, Object[] objects){
Object[] result = new Object[method.getParameterCount()];
setElement(result, bindMethod[0], matcher);
setElement(result, bindMethod[1], context);
setElement(result, bindMethod[2], reader);
setElement(result, bindMethod[3], writer);
for(int i = 0; i < objects.length; i++){
setElement(result, bindMethod[4+i], objects[i]);
}
return result;
}
private void setElement(Object[] array, int position, Object element){
if(position < 0) return;
array[position] = element;
}
private int findIndex(Class<?>[] array, Class<?> type){
for(int i = 0; i < array.length; i++){
Class<?> clazz = array[i];
if(type.isAssignableFrom(clazz)) return i;
}
return -1;
}
}

View file

@ -0,0 +1,61 @@
package dev.peerat.framework;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class RouteCachable{
private List<RouteInterceptor> interceptors;
private Map<RouteFilter, List<RouteInterceptor>> localInterceptors;
private Map<RouteFilter, Class<?>[]> types;
public RouteCachable(){
this.interceptors = new ArrayList<>();
this.localInterceptors = new HashMap<>();
this.types = new HashMap<>();
}
public void addInterceptor(RouteMapper<?>[] mappers, RouteInterceptor interceptor){
this.interceptors.add(interceptor);
for(RouteMapper<?> mapper : mappers) mapper.addInterceptor(interceptor);
}
public void addInterceptor(RouteMapper<?>[] mappers, RouteFilter filter, RouteInterceptor interceptor){
List<RouteInterceptor> list = this.localInterceptors.get(filter);
if(list == null){
list = new ArrayList<>();
this.localInterceptors.put(filter, list);
}
list.add(interceptor);
for(RouteMapper<?> mapper : mappers) mapper.addInterceptor(filter, interceptor);
}
public void bind(RouteMapper<?>[] mappers, RouteFilter filter, Class<?>... types){
Class<?>[] array = this.types.get(filter);
Class<?>[] copy;
if(array == null){
copy = types;
}else{
copy = new Class[array.length+types.length];
System.arraycopy(array, 0, copy, 0, array.length);
System.arraycopy(types, 0, copy, array.length, types.length);
}
this.types.put(filter, copy);
for(RouteMapper<?> mapper : mappers) mapper.bind(filter, types);
}
public void registerRoute(RouteState route){
if(!this.interceptors.isEmpty()) route.addInterceptors(this.interceptors);
for(Entry<RouteFilter, List<RouteInterceptor>> entry : this.localInterceptors.entrySet()){
if(entry.getKey().accept(route.getMethod(), route.getRoute(), route.getPattern())) route.addInterceptors(entry.getValue());
}
for(Entry<RouteFilter, Class<?>[]> entry : this.types.entrySet()){
if(entry.getKey().accept(route.getMethod(), route.getRoute(), route.getPattern())) route.getBinder().bind(entry.getValue());
}
}
}

View file

@ -0,0 +1,10 @@
package dev.peerat.framework;
import java.lang.reflect.Method;
import java.util.regex.Pattern;
public interface RouteFilter{
boolean accept(Method method, Route route, Pattern pattern);
}

View file

@ -5,6 +5,6 @@ import java.util.regex.Matcher;
public interface RouteInterceptor{
boolean intercept(Matcher matcher, Context context, HttpReader reader, HttpWriter writer, Method method);
boolean intercept(Matcher matcher, Context context, HttpReader reader, HttpWriter writer, Method method, Object[] params);
}

View file

@ -4,7 +4,6 @@ import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import dev.peerat.framework.utils.json.JsonMap;
@ -12,73 +11,93 @@ public class RouteMapper<U extends User>{
private Router<U> router;
private Response[] responses;
private Method[] methods;
private Route[] routes;
private Pattern[] patterns;
private RouteState[] states;
private int[] calls;
private int dif;
public RouteMapper(Router<U> router){
this.router = router;
this.responses = new Response[0];
this.methods = new Method[0];
this.routes = new Route[0];
this.patterns = new Pattern[0];
this.states = new RouteState[0];
}
public void register(Response response, Method method, Route route, Pattern pattern){
int length = responses.length+1;
this.responses = addElement(responses, new Response[length], response);
this.methods = addElement(methods, new Method[length], method);
this.routes = addElement(routes, new Route[length], route);
this.patterns = addElement(patterns, new Pattern[length], pattern);
public RouteState register(Object instance, Method method, Route route){
int length = states.length+1;
method.setAccessible(true);
RouteState state = new RouteState(instance, method, route);
this.states = addElement(states, new RouteState[length], state);
if(this.calls != null) this.calls = new int[length];
return state;
}
public void addInterceptor(RouteInterceptor interceptor){
for(RouteState state : this.states){
state.addInterceptor(interceptor);
}
}
public void addInterceptor(RouteFilter filter, RouteInterceptor interceptor){
for(RouteState state : this.states){
if(filter.accept(state.getMethod(), state.getRoute(), state.getPattern())) state.addInterceptor(interceptor);
}
}
public void bind(RouteFilter filter, Class<?>... types){
for(RouteState state : this.states){
if(filter.accept(state.getMethod(), state.getRoute(), state.getPattern())) state.getBinder().bind(types);
}
}
public void bindAll(){
for(RouteState state : this.states) state.getBinder().bindIndexes(state.getMethod());
}
public void activeReOrdering(){
this.calls = new int[responses.length];
this.calls = new int[states.length];
this.dif = 2;
}
public boolean exec(Context context, HttpReader reader, HttpWriter writer, List<RouteInterceptor> interceptors) throws Exception{
Response response = null;
Method method = null;
public boolean exec(Context context, HttpReader reader, HttpWriter writer) throws Exception{
RouteState target = null;
Matcher matcher = null;
String path = context.getPath();
synchronized(responses){
for(int i = 0; i < responses.length; i++){
Route route = routes[i];
Pattern pattern = patterns[i];
matcher = pattern.matcher(path);
if(matcher.matches()){
if(route.websocket()){
synchronized(states){
for(int i = 0; i < states.length; i++){
RouteState state = states[i];
matcher = state.match(path);
if(matcher != null){
if(state.isWebSocket()){
switchToWebSocket(reader, writer);
reader = new WebSocketReader(reader);
writer = new WebSocketWriter(writer);
if(route.needLogin()){
if(state.needLogin()){
String token = reader.<JsonMap>readJson().get("token");
context.setUser(this.router.getUser(token));
}
}
if((!context.isLogged()) && route.needLogin()){
if((!context.isLogged()) && state.needLogin()){
writer.response(401, router.getDefaultHeaders(context.getType()));
return true;
}
response = responses[i];
method = methods[i];
target = state;
order(i);
break;
}
}
}
if(response != null){
for(RouteInterceptor interceptor : interceptors){
if(!interceptor.intercept(matcher, context, reader, writer, method)) return true;
if(target != null){
Object[] params = new Object[target.getBinder().getParameterCount()];
List<RouteInterceptor> localInterceptors = target.getInterceptors();
if(localInterceptors != null){
for(RouteInterceptor interceptor : localInterceptors){
if(!interceptor.intercept(matcher, context, reader, writer, target.getMethod(), params)) return true;
}
}
response.exec(matcher, context, reader, writer);
Object[] inv = target.getBinder().bindMethod(target.getMethod(), matcher, context, reader, writer, params);
Object result = target.getMethod().invoke(target.getInstance(), inv);
if(result == null) return true;
router.getMapper().map(context, reader, writer, result);
return true;
}
return false;
@ -94,10 +113,7 @@ public class RouteMapper<U extends User>{
}
if(index < 1) return;
if(call > this.calls[index-1]){
switchElement(responses, index, index-1);
switchElement(methods, index, index-1);
switchElement(routes, index, index-1);
switchElement(patterns, index, index-1);
switchElement(states, index, index-1);
this.calls[index] = this.calls[index-1];
this.calls[index-1] = call;

View file

@ -0,0 +1,78 @@
package dev.peerat.framework;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RouteState {
private Object instance;
private Method method;
private Route route;
private Pattern pattern;
private RouteBinder binder;
private List<RouteInterceptor> interceptors; //TODO concat interceptors ??
public RouteState(Object instance, Method method, Route route){
this.instance = instance;
this.method = method;
this.route = route;
this.pattern = Pattern.compile(route.path());
this.binder = new RouteBinder();
}
public void bindIndexes(){
this.binder.bindIndexes(this.method);
}
public Matcher match(String path){
Matcher matcher = pattern.matcher(path);
return matcher.matches() ? matcher : null;
}
public void addInterceptor(RouteInterceptor interceptor){
if(this.interceptors == null) this.interceptors = new ArrayList<>();
this.interceptors.add(interceptor);
}
public void addInterceptors(Collection<RouteInterceptor> interceptors){
if(this.interceptors == null) this.interceptors = new ArrayList<>(interceptors);
else this.interceptors.addAll(interceptors);
}
public List<RouteInterceptor> getInterceptors(){
return this.interceptors;
}
public Route getRoute(){
return this.route;
}
public boolean isWebSocket(){
return this.route.websocket();
}
public boolean needLogin(){
return this.route.needLogin();
}
public Method getMethod(){
return this.method;
}
public Object getInstance(){
return this.instance;
}
public Pattern getPattern(){
return this.pattern;
}
public RouteBinder getBinder(){
return this.binder;
}
}

View file

@ -5,49 +5,26 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.Key;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Pattern;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import org.jose4j.jwa.AlgorithmConstraints.ConstraintType;
import org.jose4j.jwk.JsonWebKey.OutputControlLevel;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;
import dev.peerat.framework.auth.Authenticator;
public class Router<U extends User>{
public static void main(String[] args) {
}
public static void main(String[] args) throws Exception{}
private Locker<Context> logger;
private Locker<Throwable> exceptions;
private RouteMapper<U>[] mappers;
private List<RouteInterceptor> interceptors;
private RouteCachable cachable;
private Response noFileFound;
private RsaJsonWebKey rsaJsonWebKey;
private JwtConsumer jwtConsumer;
private Consumer<JwtClaims> claims;
private Function<JwtClaims, U> userCreator;
private Authenticator<U> auth;
private String[][] headers;
private ResponseMapper responseMapper;
private ServerSocket serverSocket;
public Router(){
@ -55,41 +32,16 @@ public class Router<U extends User>{
this.exceptions = new Locker<>();
int types = RequestType.values().length;
this.mappers = new RouteMapper[types];
this.interceptors = new ArrayList<>();
for(RequestType type : RequestType.values()) this.mappers[type.ordinal()] = new RouteMapper<>(this);
this.cachable = new RouteCachable();
this.headers = new String[types][0];
}
public Router<U> configureJwt(Consumer<JwtConsumerBuilder> consumer, Consumer<JwtClaims> claims, Function<JwtClaims, U> userCreator) throws Exception{
this.rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
configureJwtWithKey(consumer, claims, userCreator, this.rsaJsonWebKey.getKey());
public Router<U> setAuthenticator(Authenticator<U> auth){
this.auth = auth;
return this;
}
public Router<U> configureJwt(Consumer<JwtConsumerBuilder> consumer, Consumer<JwtClaims> claims, Function<JwtClaims, U> userCreator, Map<String, Object> keyParams) throws Exception{
this.rsaJsonWebKey = new RsaJsonWebKey(keyParams);
configureJwtWithKey(consumer, claims, userCreator, this.rsaJsonWebKey.getKey());
return this;
}
private void configureJwtWithKey(Consumer<JwtConsumerBuilder> consumer, Consumer<JwtClaims> claims, Function<JwtClaims, U> userCreator, Key key) throws Exception{
JwtConsumerBuilder builder = new JwtConsumerBuilder()
.setRequireExpirationTime()
.setAllowedClockSkewInSeconds(30)
.setVerificationKey(key)
.setJwsAlgorithmConstraints(ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256);
consumer.accept(builder);
this.jwtConsumer = builder.build();
this.claims = claims;
this.userCreator = userCreator;
}
public Map<String, Object> exportJwtKey(){
return this.rsaJsonWebKey.toParams(OutputControlLevel.INCLUDE_PRIVATE);
}
public Router<U> addDefaultHeaders(RequestType type, String... headers){
String[] origin = this.headers[type.ordinal()];
String[] copy = new String[origin.length+headers.length];
@ -99,25 +51,57 @@ public class Router<U extends User>{
return this;
}
public Router<U> setMapper(ResponseMapper mapper){
this.responseMapper = mapper;
return this;
}
public Router<U> register(Response response){
try{
Method method = response.getClass().getDeclaredMethod("exec",
Response.class.getDeclaredMethods()[0].getParameterTypes());
Route route = method.getAnnotation(Route.class);
this.mappers[route.type().ordinal()].register(response, method, route, Pattern.compile(route.path()));
RouteState state = this.mappers[route.type().ordinal()].register(response, method, route);
this.cachable.registerRoute(state);
System.out.println("Registered route "+method+" ["+route.type()+" "+route.path()+"]");
}catch(Exception e){
throw new IllegalArgumentException(e);
}
return this;
}
public Router<U> registerClass(Class<?> clazz, Object... injections) throws Exception{
return registerClass(clazz, new DependencyInjector().of(injections));
}
public Router<U> registerClass(Class<?> clazz, DependencyInjector injector) throws Exception{
Object instance = null;
for(Method method : clazz.getDeclaredMethods()){
Route route = method.getAnnotation(Route.class);
if(route == null) continue;
if(instance == null){
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
if(constructors.length != 1) throw new IllegalArgumentException("Class with multiple constructor. Not supported by this framework for the moment.");
instance = constructors[0].newInstance(injector.apply(constructors[0]));
}
RouteState state = this.mappers[route.type().ordinal()].register(instance, method, route);
this.cachable.registerRoute(state);
System.out.println("Registered route "+method+" ["+route.type()+" "+route.path()+"]");
}
return this;
}
public Router<U> registerPackages(Object... injections){
return registerPackages(new DependencyInjector().of(injections));
}
public Router<U> registerPackages(DependencyInjector injector){
String clazz = Thread.currentThread().getStackTrace()[2].getClassName();
if(clazz.startsWith("dev.peerat.framework")) clazz = Thread.currentThread().getStackTrace()[3].getClassName();
String pack = clazz.substring(0, clazz.lastIndexOf('.'));
return registerPackages(pack, injector);
}
@ -131,19 +115,13 @@ public class Router<U extends User>{
try{
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String line;
Map<Class<?>, Object> cache = new HashMap<>();
while((line = reader.readLine()) != null){
if(line.endsWith(".class")){
Class<?> clazz = Class.forName(name+"."+line.substring(0, line.lastIndexOf('.')));
if(Response.class.isAssignableFrom(clazz)){
if(clazz.equals(Response.class)) continue;
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
if(constructors.length != 1) continue;
injectDependencies(constructors[0], cache, injector);
}
}else{
registerPackages(name+"."+line, injector);
Class<?> clazz = Class.forName(name+"."+line.substring(0, line.length()-6));
registerClass(clazz, injector);
continue;
}
if(!line.contains(".")) registerPackages(name+"."+line, injector);
}
reader.close();
}catch(Exception e){
@ -152,26 +130,6 @@ public class Router<U extends User>{
}
return this;
}
private void injectDependencies(Constructor<?> constructor, Map<Class<?>, Object> cache, DependencyInjector injector){
Parameter[] parameters = constructor.getParameters();
Object[] injections = new Object[parameters.length];
for(int i = 0; i < parameters.length; i++){
Parameter parameter = parameters[i];
Class<?> type = parameter.getType();
Object dependency = cache.get(type);
if(dependency != null) injections[i] = dependency;
else if((injections[i] = injector.applyDependency(constructor, parameter, cache)) == null) throw new IllegalArgumentException("No dependency for type "+type+" in constructor "+constructor);
}
try {
Response response = (Response) constructor.newInstance(injections);
register(response);
System.out.println("registerd "+response.getClass());
} catch (Exception e){
e.printStackTrace();
}
}
public Router<U> setDefault(Response response){
this.noFileFound = response;
@ -179,10 +137,24 @@ public class Router<U extends User>{
}
public Router<U> addInterceptor(RouteInterceptor interceptor){
this.interceptors.add(interceptor);
this.cachable.addInterceptor(mappers, interceptor);
return this;
}
public Router<U> addInterceptor(RouteFilter filter, RouteInterceptor interceptor){
this.cachable.addInterceptor(mappers, filter, interceptor);
return this;
}
public Router<U> bind(RouteFilter filter, Class<?>... types){
this.cachable.bind(mappers, filter, types);
return this;
}
private void bindAll(){
for(RouteMapper<U> mapper : this.mappers) mapper.bindAll();
}
public Router<U> activeReOrdering(){
for(RouteMapper<?> mapper : this.mappers) mapper.activeReOrdering();
return this;
@ -191,9 +163,17 @@ public class Router<U extends User>{
String[] getDefaultHeaders(RequestType type){
return this.headers[type.ordinal()];
}
public ResponseMapper getMapper(){
return this.responseMapper;
}
public Authenticator<U> getAuthenticator(){
return this.auth;
}
void exec(Context context, HttpReader reader, HttpWriter writer) throws Exception{
if(this.mappers[context.getType().ordinal()].exec(context, reader, writer, this.interceptors)) return;
if(this.mappers[context.getType().ordinal()].exec(context, reader, writer)) return;
if(noFileFound != null) noFileFound.exec(null, context, reader, writer);
}
@ -203,25 +183,16 @@ public class Router<U extends User>{
return this;
}
public U getUser(String token) throws InvalidJwtException{
return this.userCreator.apply(this.jwtConsumer.processToClaims(token));
public U getUser(RequestType type, HttpReader reader) throws Exception{
return this.auth != null ? this.auth.getUser(type, reader) : null;
}
public U getUser(String data) throws Exception{
return this.auth != null ? this.auth.getUser(data) : null;
}
public String createAuthUser(U user) throws JoseException{
JwtClaims claims = new JwtClaims();
claims.setGeneratedJwtId(); // a unique identifier for the token
claims.setIssuedAtToNow(); // when the token was issued/created (now)
claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago)
this.claims.accept(claims);
user.write(claims);
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toJson());
jws.setKey(rsaJsonWebKey.getPrivateKey());
jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId());
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
return jws.getCompactSerialization();
public String createAuthUser(U user) throws Exception{
return this.auth.createAuthUser(user);
}
public Locker<Context> getLogger(){
@ -232,7 +203,13 @@ public class Router<U extends User>{
return this.exceptions;
}
private void checkStart(){
bindAll();
if(this.responseMapper == null) this.responseMapper = (c,r,w,o) -> {};
}
public void listen(int port, boolean ssl) throws Exception{
checkStart();
if (ssl) { // Not needed with the use of a proxy
try {
SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();

View file

@ -0,0 +1,321 @@
package dev.peerat.framework;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
public class StrictReader {
private boolean skipLF = false;
private int nextChar;
private int nChars;
private char[] cb;
private int markedChar = -1;
private int readAheadLimit = 0;
private InputStream in;
private boolean haveLeftoverChar = false;
private char leftoverChar;
private ByteBuffer bb;
private CharsetDecoder decoder;
StrictReader(InputStream in) {
this.in = in;
this.decoder = Charset.defaultCharset().newDecoder();
this.cb = new char[1];
this.nextChar = this.nChars = 0;
this.bb = ByteBuffer.allocate(1);
this.bb.flip();
}
public boolean ready() throws IOException {
synchronized (this.in) {
ensureOpen();
if (this.skipLF) {
if (this.nextChar >= this.nChars && sdReady())
fill();
if (this.nextChar < this.nChars) {
if (this.cb[this.nextChar] == '\n')
this.nextChar++;
this.skipLF = false;
}
}
return (this.nextChar < this.nChars || sdReady());
}
}
public boolean sdReady() throws IOException {
synchronized (this.in) {
ensureOpen();
return (this.haveLeftoverChar || implReady());
}
}
public int read(char[] paramArrayOfchar, int paramInt1, int paramInt2) throws IOException {
if (paramInt1 < 0 || paramInt1 > paramArrayOfchar.length || paramInt2 < 0
|| paramInt1 + paramInt2 > paramArrayOfchar.length || paramInt1 + paramInt2 < 0)
throw new IndexOutOfBoundsException();
synchronized (this.in) {
ensureOpen();
if (paramInt2 == 0)
return 0;
int i = read1(paramArrayOfchar, paramInt1, paramInt2);
if (i <= 0)
return i;
while (i < paramInt2 && sdReady()) {
int j = read1(paramArrayOfchar, paramInt1 + i, paramInt2 - i);
if (j <= 0)
break;
i += j;
}
return i;
}
}
private int read1(char[] paramArrayOfchar, int paramInt1, int paramInt2) throws IOException {
if (this.nextChar >= this.nChars) {
if (paramInt2 >= this.cb.length && this.markedChar <= -1 && !this.skipLF)
return sdRead(paramArrayOfchar, paramInt1, paramInt2);
fill();
}
if (this.nextChar >= this.nChars)
return -1;
if (this.skipLF) {
this.skipLF = false;
if (this.cb[this.nextChar] == '\n') {
this.nextChar++;
if (this.nextChar >= this.nChars)
fill();
if (this.nextChar >= this.nChars)
return -1;
}
}
int i = Math.min(paramInt2, this.nChars - this.nextChar);
System.arraycopy(this.cb, this.nextChar, paramArrayOfchar, paramInt1, i);
this.nextChar += i;
return i;
}
private void ensureOpen() throws IOException {
if (this.in == null)
throw new IOException("Stream closed");
}
public String readLines() throws IOException{
StringBuilder builder = new StringBuilder();
synchronized (this.in){
ensureOpen();
}
return builder.toString();
}
public String readLine() throws IOException {
StringBuilder stringBuilder = null;
synchronized (this.in) {
ensureOpen();
boolean bool = this.skipLF;
while (true) {
if (this.nextChar >= this.nChars)
fill();
if (this.nextChar >= this.nChars) {
if (stringBuilder != null && stringBuilder.length() > 0)
return stringBuilder.toString();
return null;
}
boolean bool1 = false;
char c = Character.MIN_VALUE;
if (bool && this.cb[this.nextChar] == '\n')
this.nextChar++;
this.skipLF = false;
bool = false;
int j;
for (j = this.nextChar; j < this.nChars; j++) {
c = this.cb[j];
if (c == '\n' || c == '\r') {
bool1 = true;
break;
}
}
int i = this.nextChar;
this.nextChar = j;
if (bool1) {
String str;
if (stringBuilder == null) {
str = new String(this.cb, i, j - i);
} else {
stringBuilder.append(this.cb, i, j - i);
str = stringBuilder.toString();
}
this.nextChar++;
if (c == '\r')
this.skipLF = true;
return str;
}
if (stringBuilder == null)
stringBuilder = new StringBuilder();
stringBuilder.append(this.cb, i, j - i);
}
}
}
private void fill() throws IOException {
int i;
if (this.markedChar <= -1) {
i = 0;
} else {
int j = this.nextChar - this.markedChar;
if (j >= this.readAheadLimit) {
this.markedChar = -2;
this.readAheadLimit = 0;
i = 0;
} else {
if (this.readAheadLimit <= this.cb.length) {
System.arraycopy(this.cb, this.markedChar, this.cb, 0, j);
this.markedChar = 0;
i = j;
} else {
char[] arrayOfChar = new char[this.readAheadLimit];
System.arraycopy(this.cb, this.markedChar, arrayOfChar, 0, j);
this.cb = arrayOfChar;
this.markedChar = 0;
i = j;
}
this.nextChar = this.nChars = j;
}
}
while (true) {
int j = sdRead(this.cb, i, this.cb.length - i);
if (j != 0) {
if (j > 0) {
this.nChars = i + j;
this.nextChar = i;
}
return;
}
}
}
// StreamDecoder
private int sdRead(char[] paramArrayOfchar, int paramInt1, int paramInt2) throws IOException {
int i = paramInt1;
int j = paramInt2;
if (i < 0 || i > paramArrayOfchar.length || j < 0 || i + j > paramArrayOfchar.length || i + j < 0)
throw new IndexOutOfBoundsException();
synchronized (this.in) {
ensureOpen();
if (j == 0)
return 0;
byte b = 0;
if (this.haveLeftoverChar) {
paramArrayOfchar[i] = this.leftoverChar;
i++;
j--;
this.haveLeftoverChar = false;
b = 1;
if (j == 0 || !implReady())
return b;
}
if (j == 1) {
int k = read0();
if (k == -1)
return b == 0 ? -1 : b;
paramArrayOfchar[i] = (char) k;
return b + 1;
}
return b + implRead(paramArrayOfchar, i, i + j);
}
}
boolean implReady() {
return (this.bb.hasRemaining() || inReady());
}
private boolean inReady() {
try {
return (this.in != null && this.in.available() > 0);
} catch (IOException iOException) {
return false;
}
}
int implRead(char[] paramArrayOfchar, int paramInt1, int paramInt2) throws IOException {
CharBuffer charBuffer = CharBuffer.wrap(paramArrayOfchar, paramInt1, paramInt2 - paramInt1);
if (charBuffer.position() != 0)
charBuffer = charBuffer.slice();
boolean bool = false;
while (true) {
CoderResult coderResult = this.decoder.decode(this.bb, charBuffer, bool);
if (coderResult.isUnderflow()) {
if (bool || !charBuffer.hasRemaining() || (charBuffer.position() > 0 && !inReady()))
break;
int i = readBytes();
if (i < 0) {
bool = true;
if (charBuffer.position() == 0 && !this.bb.hasRemaining())
break;
this.decoder.reset();
}
continue;
}
if (coderResult.isOverflow()) {
break;
}
coderResult.throwException();
}
if (bool)
this.decoder.reset();
if (charBuffer.position() == 0) {
if (bool)
return -1;
}
return charBuffer.position();
}
private int read0() throws IOException {
synchronized (this.in) {
if (this.haveLeftoverChar) {
this.haveLeftoverChar = false;
return this.leftoverChar;
}
char[] arrayOfChar = new char[2];
int i = sdRead(arrayOfChar, 0, 2);
switch (i) {
case -1:
return -1;
case 2:
this.leftoverChar = arrayOfChar[1];
this.haveLeftoverChar = true;
case 1:
return arrayOfChar[0];
}
return -1;
}
}
private int readBytes() throws IOException {
this.bb.compact();
try {
int j = this.bb.limit();
int k = this.bb.position();
int b = (k <= j) ? (j - k) : 0;
int m = this.in.read(this.bb.array(), this.bb.arrayOffset() + k, b);
if (m < 0)
return m;
if (m == 0)
throw new IOException("Underlying input stream returned zero bytes");
this.bb.position(k + m);
} finally {
this.bb.flip();
}
int i = this.bb.remaining();
return i;
}
}

View file

@ -1,9 +1,3 @@
package dev.peerat.framework;
import org.jose4j.jwt.JwtClaims;
public abstract class User{
public abstract void write(JwtClaims claims);
}
public abstract class User{}

View file

@ -0,0 +1,17 @@
package dev.peerat.framework.auth;
public class AuthException extends Exception{
public AuthException(String message){
super(message);
}
public AuthException(Throwable throwable){
super(throwable);
}
public AuthException(String message, Throwable throwable){
super(message, throwable);
}
}

View file

@ -0,0 +1,15 @@
package dev.peerat.framework.auth;
import dev.peerat.framework.HttpReader;
import dev.peerat.framework.RequestType;
import dev.peerat.framework.User;
public interface Authenticator<U extends User>{
String createAuthUser(U user) throws Exception;
U getUser(RequestType type, HttpReader reader) throws Exception;
U getUser(String data) throws Exception;
}

View file

@ -0,0 +1,94 @@
package dev.peerat.framework.auth;
import java.security.Key;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import org.jose4j.jwa.AlgorithmConstraints.ConstraintType;
import org.jose4j.jwk.JsonWebKey.OutputControlLevel;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import dev.peerat.framework.HttpReader;
import dev.peerat.framework.RequestType;
import dev.peerat.framework.User;
public class JwtAuthenticator<U extends User> implements Authenticator<U>{
private RsaJsonWebKey rsaJsonWebKey;
private JwtConsumer jwtConsumer;
private BiConsumer<JwtClaims, U> claims;
private Function<JwtClaims, U> userCreator;
public JwtAuthenticator(){}
public JwtAuthenticator<U> configure(Consumer<JwtConsumerBuilder> consumer, BiConsumer<JwtClaims, U> claims, Function<JwtClaims, U> userCreator) throws Exception{
this.rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
configureWithKey(consumer, claims, userCreator, this.rsaJsonWebKey.getKey());
return this;
}
public JwtAuthenticator<U> configure(Consumer<JwtConsumerBuilder> consumer, BiConsumer<JwtClaims, U> claims, Function<JwtClaims, U> userCreator, Map<String, Object> keyParams) throws Exception{
this.rsaJsonWebKey = new RsaJsonWebKey(keyParams);
configureWithKey(consumer, claims, userCreator, this.rsaJsonWebKey.getKey());
return this;
}
private JwtAuthenticator<U> configureWithKey(Consumer<JwtConsumerBuilder> consumer, BiConsumer<JwtClaims, U> claims, Function<JwtClaims, U> userCreator, Key key) throws Exception{
JwtConsumerBuilder builder = new JwtConsumerBuilder()
.setRequireExpirationTime()
.setAllowedClockSkewInSeconds(30)
.setVerificationKey(key)
.setJwsAlgorithmConstraints(ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256);
consumer.accept(builder);
this.jwtConsumer = builder.build();
this.claims = claims;
this.userCreator = userCreator;
return this;
}
public Map<String, Object> exportKey(){
return this.rsaJsonWebKey.toParams(OutputControlLevel.INCLUDE_PRIVATE);
}
@Override
public String createAuthUser(U user) throws Exception{
JwtClaims claims = new JwtClaims();
claims.setGeneratedJwtId(); // a unique identifier for the token
claims.setIssuedAtToNow(); // when the token was issued/created (now)
claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago)
this.claims.accept(claims, user);
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toJson());
jws.setKey(rsaJsonWebKey.getPrivateKey());
jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId());
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
return jws.getCompactSerialization();
}
@Override
public U getUser(RequestType type, HttpReader reader) throws Exception{
String auth = reader.getHeader("Authorization");
if(auth == null) return null;
auth = auth.substring(7);
return this.userCreator.apply(this.jwtConsumer.processToClaims(auth));
}
@Override
public U getUser(String data) throws Exception {
if(data == null) return null;
return this.userCreator.apply(this.jwtConsumer.processToClaims(data));
}
}

View file

@ -3,16 +3,25 @@ package dev.peerat.framework.utils.json;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public class JsonArray extends Json{
private List<Object> list;
private Collection<Object> list;
public JsonArray(){
this.list = new ArrayList<>();
}
public JsonArray(Collection<Object> collection){
this();
this.list.addAll(collection);
}
public JsonArray(Iterator<Object> iterator){
this();
while(iterator.hasNext()) this.list.add(iterator.next());
}
public <E> Collection<E> toList(){
return (Collection<E>) this.list;
}

View file

@ -14,6 +14,11 @@ public class JsonMap extends Json{
this.map = new HashMap<>();
}
public JsonMap(Map<String, Object> map){
this();
for(Entry<String, Object> entry : map.entrySet()) this.map.put(entry.getKey(), entry.getValue());
}
public Set<Entry<String, Object>> entries(){
return this.map.entrySet();
}

View file

@ -1,16 +1,17 @@
package dev.peerat.framework.utils.json;
import java.util.ArrayList;
import static dev.peerat.parser.TokenType.*;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import be.jeffcheasey88.peeratcode.parser.Parser;
import be.jeffcheasey88.peeratcode.parser.Token;
import be.jeffcheasey88.peeratcode.parser.TokenType;
import be.jeffcheasey88.peeratcode.parser.state.RedirectStateTree;
import be.jeffcheasey88.peeratcode.parser.state.StateTree;
import dev.peerat.parser.Parser;
import dev.peerat.parser.Token;
import dev.peerat.parser.state.RedirectStateTree;
import dev.peerat.parser.state.StateTree;
public class JsonParser extends Parser<Json>{
@ -25,7 +26,7 @@ public class JsonParser extends Parser<Json>{
StateTree<Json> content_array_element = content_array.then(new RedirectStateTree<>(content, (global, local) ->{
List<Object> list = global.get();
if(list == null){
list = new ArrayList<>();
list = new LinkedList<>();
global.set(list);
}
list.add(local.get());
@ -43,16 +44,16 @@ public class JsonParser extends Parser<Json>{
.then(content_array_element);
content.then(new RedirectStateTree<>(base, (global, local) -> global.set(local.get()))).end();
content.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.STRING), (bag, token) -> bag.set(token))).end();
content.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.CHAR), (bag, token) -> bag.set(token))).end();
StateTree<Json> number = content.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.NAME), (bag, token) -> bag.set(token)));
content.then((validator) -> validator.validate((token) -> token.getType().equals(STRING), (bag, token) -> bag.set(token))).end();
content.then((validator) -> validator.validate((token) -> token.getType().equals(CHAR), (bag, token) -> bag.set(token))).end();
StateTree<Json> number = content.then((validator) -> validator.validate((token) -> token.getType().equals(NAME), (bag, token) -> bag.set(token)));
number.end();
number.then((validator) -> validator.validate((token) -> token.getValue().equals("."), (bag, token) -> bag.set(bag.<Token>get().concat(token))))
.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.NAME), (bag, token) -> bag.set(bag.<Token>get().concat(token))))
.then((validator) -> validator.validate((token) -> token.getType().equals(NAME), (bag, token) -> bag.set(bag.<Token>get().concat(token))))
.end();
StateTree<Json> mapper = new StateTree<>();
StateTree<Json> mapper_key = mapper.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.STRING), (bag, token) -> {
StateTree<Json> mapper_key = mapper.then((validator) -> validator.validate((token) -> token.getType().equals(STRING), (bag, token) -> {
Map<Token, Object> map = bag.get("map");
if(map == null){
map = new HashMap<>();
@ -105,20 +106,20 @@ public class JsonParser extends Parser<Json>{
if(value instanceof Token){
Token token = (Token) value;
String content = token.getValue();
if(token.getType().equals(TokenType.STRING)){
if(token.getType().equals(STRING)){
return content;
}else if(token.getType().equals(TokenType.CHAR)){
}else if(token.getType().equals(CHAR)){
return content.charAt(0);
}else{
try{
return Long.parseLong(content);
}catch(Exception _){
}catch(Exception e){
try {
return Double.parseDouble(content);
}catch(Exception __){
}catch(Exception ex){
try{
return Boolean.parseBoolean(content);
}catch(Exception ___){}
}catch(Exception exc){}
}
}
}

View file

@ -1,13 +1,14 @@
package dev.peerat.framework.utils.json;
import be.jeffcheasey88.peeratcode.parser.Token;
import be.jeffcheasey88.peeratcode.parser.TokenType;
import be.jeffcheasey88.peeratcode.parser.Tokenizer;
import dev.peerat.parser.Token;
import dev.peerat.parser.TokenType;
import dev.peerat.parser.Tokenizer;
public class JsonTokenizer extends Tokenizer{
@Override
public void parse(String line){
long time = System.currentTimeMillis();
for(int i = 0; i < line.length(); i++){
char c = line.charAt(i);