Skip to content

Commit

Permalink
0.2.0 - rename OBJECT, ARRAY to jObject, jArray.
Browse files Browse the repository at this point in the history
Add 'custom' to function describer (differentiate between internal functions and client added ones)
  • Loading branch information
elisherer committed Apr 14, 2024
1 parent 0ca645b commit c20478e
Show file tree
Hide file tree
Showing 46 changed files with 483 additions and 375 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ Maven
<dependency>
<groupId>co.nlighten</groupId>
<artifactId>json-transform</artifactId>
<version>0.1.1</version>
<version>0.2.0</version>
</dependency>
```

Gradle
```groovy
implementation 'co.nlighten:json-transform:0.1.1'
implementation 'co.nlighten:json-transform:0.2.0'
```

## License
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
}

group 'co.nlighten'
version = '0.1.1'
version = '0.2.0'

ext {
gsonVersion = "2.10.1"
Expand Down
101 changes: 56 additions & 45 deletions src/main/java/co/nlighten/jsontransform/FunctionsDescriber.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import co.nlighten.jsontransform.adapters.JsonAdapter;
import co.nlighten.jsontransform.adapters.JsonArrayAdapter;
import co.nlighten.jsontransform.adapters.JsonObjectAdapter;
import co.nlighten.jsontransform.functions.TransformerFunctionRaw;
import co.nlighten.jsontransform.functions.annotations.*;
import co.nlighten.jsontransform.functions.common.ArgType;
import co.nlighten.jsontransform.functions.common.TransformerFunction;
Expand All @@ -13,70 +14,72 @@

public class FunctionsDescriber<JE, JA extends Iterable<JE>, JO extends JE> {

private final JsonObjectAdapter<JE,JA,JO> OBJECT;
private final JsonArrayAdapter<JE, JA, JO> ARRAY;
private final String internalPackageName;
private final JsonObjectAdapter<JE,JA,JO> jObject;
private final JsonArrayAdapter<JE, JA, JO> jArray;
private final JsonAdapter<JE, JA, JO> adapter;

public FunctionsDescriber(JsonAdapter<JE, JA, JO> jsonAdapter) {
this.adapter = jsonAdapter;
this.ARRAY = jsonAdapter.ARRAY;
this.OBJECT = jsonAdapter.OBJECT;
this.jArray = jsonAdapter.jArray;
this.jObject = jsonAdapter.jObject;
this.internalPackageName = TransformerFunctionRaw.class.getPackageName();
}

private JO convertToSchema(ArgType argType) {
if (argType == ArgType.Any || argType == ArgType.Transformer) return null;
var res = OBJECT.create();
var res = jObject.create();
String type;
switch (argType) {
case Enum -> {
OBJECT.add(res, "enum", ARRAY.create());
jObject.add(res, "enum", jArray.create());
type = "string";
}
case BigDecimal -> {
OBJECT.add(res, "$comment", argType.name());
jObject.add(res, "$comment", argType.name());
type = "number";
}
case Long -> {
OBJECT.add(res, "$comment", argType.name());
jObject.add(res, "$comment", argType.name());
type = "integer";
}
case ArrayOfArray -> {
OBJECT.add(res, "items", convertToSchema(ArgType.Array));
jObject.add(res, "items", convertToSchema(ArgType.Array));
type = "array";
}
case ArrayOfBigDecimal -> {
OBJECT.add(res, "items", convertToSchema(ArgType.BigDecimal));
jObject.add(res, "items", convertToSchema(ArgType.BigDecimal));
type = "array";
}
case ArrayOfString -> {
OBJECT.add(res, "items", convertToSchema(ArgType.String));
jObject.add(res, "items", convertToSchema(ArgType.String));
type = "array";
}
default -> {
type = argType.name().toLowerCase();
}
}
OBJECT.add(res, "type", type);
jObject.add(res, "type", type);
return res;
}

private JO oneOf(ArgType[] types) {
var res = OBJECT.create();
var of = ARRAY.create();
Arrays.stream(types).forEach(argType -> ARRAY.add(of, convertToSchema(argType)));
OBJECT.add(res, "oneOf", of);
var res = jObject.create();
var of = jArray.create();
Arrays.stream(types).forEach(argType -> jArray.add(of, convertToSchema(argType)));
jObject.add(res, "oneOf", of);
return res;
}

private JO describe(TransformerFunction<JE, JA, JO> function) {
var jo = OBJECT.create();
var jo = jObject.create();
// description
var desc = function.getClass().getAnnotationsByType(Documentation.class);
if (desc.length > 0) {
var ann = Arrays.stream(desc).findFirst().get();
OBJECT.add(jo, "description", ann.value());
jObject.add(jo, "description", ann.value());
if (ann.notes() != null && !ann.notes().isEmpty()) {
OBJECT.add(jo, "notes", ann.notes());
jObject.add(jo, "notes", ann.notes());
}
}
// inputSchema
Expand All @@ -86,11 +89,11 @@ private JO describe(TransformerFunction<JE, JA, JO> function) {
var inputSchema = inputTypeArr.length == 1 ? convertToSchema(inputTypeArr[0]) : oneOf(inputTypeArr);
if (inputType.description() != null && !inputType.description().isEmpty()) {
if (inputSchema == null) { // probably "Any"
inputSchema = OBJECT.create();
inputSchema = jObject.create();
}
OBJECT.add(inputSchema, "description", inputType.description());
jObject.add(inputSchema, "description", inputType.description());
}
OBJECT.add(jo, "inputSchema", inputSchema);
jObject.add(jo, "inputSchema", inputSchema);
}
// outputSchema
var outputType = Arrays.stream(function.getClass().getAnnotationsByType(OutputType.class)).findFirst().orElse(null);
Expand All @@ -99,82 +102,90 @@ private JO describe(TransformerFunction<JE, JA, JO> function) {
var outputSchema = outputTypeArr.length == 1 ? convertToSchema(outputTypeArr[0]) : oneOf(outputTypeArr);
if (outputType.description() != null && !outputType.description().isEmpty()) {
if (outputSchema == null) { // probably "Any"
outputSchema = OBJECT.create();
outputSchema = jObject.create();
}
OBJECT.add(outputSchema, "description", outputType.description());
jObject.add(outputSchema, "description", outputType.description());
}
OBJECT.add(jo, "outputSchema", outputSchema);
jObject.add(jo, "outputSchema", outputSchema);
}

// arguments
var args = function.getArguments();
if (args != null && !args.isEmpty()) {
var arguments = ARRAY.create();
var arguments = jArray.create();
for (var entry : args.entrySet()) {
var arg = entry.getKey();
var val = entry.getValue();
var ao = OBJECT.create();
OBJECT.add(ao, "name", arg);
var ao = jObject.create();
jObject.add(ao, "name", arg);
if (val.description() != null) {
OBJECT.add(ao, "description", val.description());
jObject.add(ao, "description", val.description());
}
OBJECT.add(ao, "type", val.type().name().toLowerCase());
jObject.add(ao, "type", val.type().name().toLowerCase());
if (val.enumValues().length > 0) {
var enumValues = ARRAY.create();
Arrays.stream(val.enumValues()).forEach(item -> ARRAY.add(enumValues, item));
OBJECT.add(ao, "enum", enumValues);
var enumValues = jArray.create();
Arrays.stream(val.enumValues()).forEach(item -> jArray.add(enumValues, item));
jObject.add(ao, "enum", enumValues);
}
if (val.position() > -1) {
OBJECT.add(ao, "position", val.position());
jObject.add(ao, "position", val.position());
}
if (val.required()) {
OBJECT.add(ao, "required", true);
jObject.add(ao, "required", true);
} else {
var defaultValue = function.getDefaultValue(arg);
if (defaultValue != null) {
OBJECT.add(ao, "default", adapter.wrap(defaultValue));
jObject.add(ao, "default", adapter.wrap(defaultValue));
}
}
ARRAY.add(arguments, ao);
jArray.add(arguments, ao);
}
OBJECT.add(jo, "arguments", arguments);
jObject.add(jo, "arguments", arguments);
}
// pipedType
var piped = function.getClass().getAnnotationsByType(TypeIsPiped.class);
if (piped.length > 0) {
OBJECT.add(jo, "pipedType", true);
jObject.add(jo, "pipedType", true);
}
// custom
var packageName = function.getClass().getPackageName();
if (!internalPackageName.equals(packageName)) {
jObject.add(jo, "custom", true);
}
return jo;
}

public JO describe(Map<String, TransformerFunction<JE, JA, JO>> functions) {
var result = OBJECT.create();
var result = jObject.create();
var keys = functions.keySet().stream().sorted().toList();
for (String key : keys) {
var func = functions.get(key);
var desc = describe(func);

// aliasTo
var aliases = Arrays.stream(func.getClass().getAnnotationsByType(
Aliases.class)).findFirst().orElse(null);
if (aliases != null) {
var aliasesValue = aliases.value();
for (int i = 1; i < aliasesValue.length; i++) {
if (Objects.equals(aliasesValue[i], key)) {
OBJECT.add(desc, "aliasTo", aliasesValue[0]);
jObject.add(desc, "aliasTo", aliasesValue[0]);
break;
}
}
}

// deprecated
var deprecatedAlias = Arrays.stream(func.getClass().getAnnotationsByType(
DeprecatedAlias.class)).findFirst().orElse(null);
if (deprecatedAlias != null && deprecatedAlias.value().equals(key)) {
if (aliases != null) {
OBJECT.add(desc, "deprecated", aliases.value()[0]);
jObject.add(desc, "deprecated", aliases.value()[0]);
} else {
OBJECT.add(desc, "deprecated", true);
jObject.add(desc, "deprecated", true);
}
}

OBJECT.add(result, key, desc);
jObject.add(result, key, desc);
}
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ private JsonElementStreamer(FunctionContext<JE, JA, JO> context, Stream<JE> stre
}

public boolean knownAsEmpty() {
return value != null && this.context.ARRAY.isEmpty(value);
return value != null && this.context.jArray.isEmpty(value);
}

public Stream<JE> stream() {
Expand Down Expand Up @@ -68,9 +68,9 @@ public JA toJsonArray() {
if (value != null) {
return value;
}
var ja = context.ARRAY.create();
var ja = context.jArray.create();
if (stream != null) {
stream.forEach(item -> context.ARRAY.add(ja, item));
stream.forEach(item -> context.jArray.add(ja, item));
}
return ja;
}
Expand Down
38 changes: 18 additions & 20 deletions src/main/java/co/nlighten/jsontransform/JsonTransformer.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package co.nlighten.jsontransform;

import co.nlighten.jsontransform.*;
import co.nlighten.jsontransform.ParameterResolver;
import co.nlighten.jsontransform.adapters.JsonAdapter;
import com.google.gson.JsonNull;

Expand Down Expand Up @@ -69,36 +67,36 @@ protected Object fromJsonObject(JO definition, co.nlighten.jsontransform.Paramet
: adapter.wrap(res);
}

var result = adapter.OBJECT.create();
if (adapter.OBJECT.has(definition, "*")) {
var val = adapter.OBJECT.get(definition, "*");
var result = adapter.jObject.create();
if (adapter.jObject.has(definition, "*")) {
var val = adapter.jObject.get(definition, "*");
var res = (JE) fromJsonElement(val, resolver, false);
if (res != null) {
var isArray = adapter.ARRAY.is(val);
if (isArray && adapter.ARRAY.is(res)) {
var isArray = adapter.jArray.is(val);
if (isArray && adapter.jArray.is(res)) {
for (JE x: (JA)res) {
if (adapter.OBJECT.is(x)) {
if (adapter.jObject.is(x)) {
var xo = (JO)x;
for (Map.Entry<String, JE> e : adapter.OBJECT.entrySet(xo)) {
adapter.OBJECT.add(result, e.getKey(), e.getValue());
for (Map.Entry<String, JE> e : adapter.jObject.entrySet(xo)) {
adapter.jObject.add(result, e.getKey(), e.getValue());
}
}
}
} else if (adapter.OBJECT.is(res)) {
} else if (adapter.jObject.is(res)) {
// override the base object with resolved one
result = (JO)res;
} else {
adapter.OBJECT.add(result, "*", res);
adapter.jObject.add(result, "*", res);
}
}
}

for (Map.Entry<String, JE> kv : adapter.OBJECT.entrySet(definition)) {
for (Map.Entry<String, JE> kv : adapter.jObject.entrySet(definition)) {
if (kv.getKey().equals("*")) continue;
var localKey = kv.getKey();
var value = (JE) fromJsonElement(kv.getValue(), resolver, false);
if (!adapter.isNull(value) || adapter.OBJECT.has(result, localKey) /* we allow overriding with null*/) {
adapter.OBJECT.add(result, localKey, value);
if (!adapter.isNull(value) || adapter.jObject.has(result, localKey) /* we allow overriding with null*/) {
adapter.jObject.add(result, localKey, value);
}
}

Expand All @@ -108,14 +106,14 @@ protected Object fromJsonObject(JO definition, co.nlighten.jsontransform.Paramet
protected Object fromJsonElement(JE definition, ParameterResolver resolver, boolean allowReturningStreams) {
if (adapter.isNull(definition))
return adapter.jsonNull();
if (adapter.ARRAY.is(definition)) {
var result = adapter.ARRAY.create();
adapter.ARRAY.stream((JA)definition)
if (adapter.jArray.is(definition)) {
var result = adapter.jArray.create();
adapter.jArray.stream((JA)definition)
.map(d -> (JE)fromJsonElement(d, resolver, false))
.forEachOrdered(item -> adapter.ARRAY.add(result, item));
.forEachOrdered(item -> adapter.jArray.add(result, item));
return result;
}
if (adapter.OBJECT.is(definition)) {
if (adapter.jObject.is(definition)) {
return fromJsonObject((JO)definition, resolver, allowReturningStreams);
}
return fromJsonPrimitive(definition, resolver, allowReturningStreams);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package co.nlighten.jsontransform;

import co.nlighten.jsontransform.ParameterResolver;

@FunctionalInterface
public interface JsonTransformerFunction<JE> {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public FunctionMatchResult<Object> matchObject(JO definition, co.nlighten.jsontr
// look for an object function
// (precedence is all internal functions sorted alphabetically first, then client added ones second, by registration order)
for (String key : functions.keySet()) {
if (jsonAdapter.OBJECT.has(definition, FUNCTION_KEY_PREFIX + key)) {
if (jsonAdapter.jObject.has(definition, FUNCTION_KEY_PREFIX + key)) {
var func = functions.get(key);
var context = new ObjectFunctionContext<>(
definition,
Expand Down
Loading

0 comments on commit c20478e

Please sign in to comment.