Skip to content

Commit

Permalink
feat: implement reflection for Reflectable annotated classes. Improve…
Browse files Browse the repository at this point in the history
… ObjectMapper and implement JsonSerializer.
  • Loading branch information
Zurcusa committed Aug 13, 2024
1 parent 227faf1 commit 45b64b9
Show file tree
Hide file tree
Showing 10 changed files with 271 additions and 92 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ dependencies {
implementation("org.projectlombok:lombok:1.18.34")

implementation("org.teavm:teavm-jso-apis:0.10.0")

implementation("org.teavm:teavm-core:0.10.0")
}

teavm.js {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/limechain/chain/spec/ChainSpec.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.limechain.chain.spec;

import com.limechain.teavm.annotation.Reflectable;
import com.limechain.utils.json.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
Expand All @@ -13,6 +14,7 @@
*/
@Getter
@Setter
@Reflectable
public class ChainSpec implements Serializable {
private String id;
private String name;
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/com/limechain/teavm/HttpRequest.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package com.limechain.teavm;

import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;

public class HttpRequest {
@JSBody(params = {"method", "url", "body"}, script = "return httpRequestSync(method, url, body);")
public static native String httpRequestSync(String method, String url, JSObject body);
public static native String httpRequestSync(String method, String url, String body);
}
61 changes: 61 additions & 0 deletions src/main/java/com/limechain/teavm/ReflectionSupplierImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.limechain.teavm;

import com.limechain.teavm.annotation.Reflectable;
import org.teavm.classlib.ReflectionContext;
import org.teavm.classlib.ReflectionSupplier;
import org.teavm.model.ClassReader;
import org.teavm.model.FieldReader;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ReflectionSupplierImpl implements ReflectionSupplier {
@Override
public Collection<String> getAccessibleFields(ReflectionContext context, String className) {
ClassReader cls = context.getClassSource().get(className);
if (cls == null) {
return Collections.emptyList();
}
Set<String> fields = new HashSet<>();
if (cls.getAnnotations().get(Reflectable.class.getName()) != null) {
List<String> descriptors = cls.getFields().stream()
.map(FieldReader::getName)
.toList();
fields.addAll(descriptors);
} else {
for (FieldReader field : cls.getFields()) {
if (field.getAnnotations().get(Reflectable.class.getName()) != null) {
fields.add(field.getName());
}
}
}
return fields;
}

@Override
public Collection<MethodDescriptor> getAccessibleMethods(ReflectionContext context, String className) {
ClassReader cls = context.getClassSource().get(className);
if (cls == null) {
return Collections.emptyList();
}
Set<MethodDescriptor> methods = new HashSet<>();
if (cls.getAnnotations().get(Reflectable.class.getName()) != null) {
List<MethodDescriptor> descriptors = cls.getMethods().stream()
.map(MethodReader::getDescriptor)
.toList();
methods.addAll(descriptors);
} else {
for (MethodReader method : cls.getMethods()) {
if (method.getAnnotations().get(Reflectable.class.getName()) != null) {
methods.add(method.getDescriptor());
}
}
}
return methods;
}
}
26 changes: 26 additions & 0 deletions src/main/java/com/limechain/teavm/annotation/Reflectable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2016 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.limechain.teavm.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE})
public @interface Reflectable {
}
112 changes: 112 additions & 0 deletions src/main/java/com/limechain/utils/json/JsonSerializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package com.limechain.utils.json;

import com.limechain.utils.DivLogger;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;

public class JsonSerializer {

private final static DivLogger LOGGER = new DivLogger();

// Method to serialize any object to a JSON string
public static String serializeToJson(Object object) {
StringBuilder jsonBuilder = new StringBuilder();
jsonBuilder.append("{");

Field[] fields = object.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
try {
Field field = fields[i]; // To access private fields
field.setAccessible(true);
String fieldName = field.getName();
Object fieldValue = field.get(object);

jsonBuilder.append("\"").append(fieldName).append("\":");
appendValue(jsonBuilder, fieldValue);

if (i < fields.length - 1) {
jsonBuilder.append(",");
}
} catch (IllegalAccessException e) {
LOGGER.log(Level.FINE, Arrays.toString(e.getStackTrace()));
}
}

jsonBuilder.append("}");
return jsonBuilder.toString();
}

// Helper method to handle different types of values
private static void appendValue(StringBuilder jsonBuilder, Object value) {
if (value == null) {
jsonBuilder.append("null");
} else if (value instanceof String) {
jsonBuilder.append("\"").append(value).append("\"");
} else if (value instanceof Number || value instanceof Boolean) {
jsonBuilder.append(value);
} else if (value instanceof List) {
appendList(jsonBuilder, (List<?>) value);
} else if (value instanceof Map) {
appendMap(jsonBuilder, (Map<?, ?>) value);
} else if (value instanceof byte[]) {
appendByteArray(jsonBuilder, (byte[]) value);
} else if (value.getClass().isArray()) {
appendArray(jsonBuilder, value);
} else {
jsonBuilder.append(serializeToJson(value)); // Recursively handle nested objects
}
}

// Method to serialize a List to JSON
private static void appendList(StringBuilder jsonBuilder, List<?> list) {
jsonBuilder.append("[");
for (int i = 0; i < list.size(); i++) {
appendValue(jsonBuilder, list.get(i));
if (i < list.size() - 1) {
jsonBuilder.append(",");
}
}
jsonBuilder.append("]");
}

// Method to serialize a Map to JSON
private static void appendMap(StringBuilder jsonBuilder, Map<?, ?> map) {
jsonBuilder.append("{");
Set<?> keys = map.keySet();
int i = 0;
for (Object key : keys) {
jsonBuilder.append("\"").append(key).append("\":");
appendValue(jsonBuilder, map.get(key));
if (i < keys.size() - 1) {
jsonBuilder.append(",");
}
i++;
}
jsonBuilder.append("}");
}

// Method to serialize an array to JSON
private static void appendArray(StringBuilder jsonBuilder, Object array) {
jsonBuilder.append("[");
int length = Array.getLength(array);
for (int i = 0; i < length; i++) {
appendValue(jsonBuilder, Array.get(array, i));
if (i < length - 1) {
jsonBuilder.append(",");
}
}
jsonBuilder.append("]");
}

private static void appendByteArray(StringBuilder jsonBuilder, byte[] byteArray) {
String base64 = Base64.getEncoder().encodeToString(byteArray);
jsonBuilder.append("\"").append(base64).append("\"");
}
}
4 changes: 4 additions & 0 deletions src/main/java/com/limechain/utils/json/JsonUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ static Map<String, Object> parseJson(String jsonPath) {
return new JsonParser(readJsonFromFile(jsonPath)).parse();
}

public String stringify(Object object) {
return JsonSerializer.serializeToJson(object);
}

private static String readJsonFromFile(String filePath) {
return HttpRequest.httpRequestSync("GET", filePath, null);
}
Expand Down
Loading

0 comments on commit 45b64b9

Please sign in to comment.