// Path: src/main/java/ioinformarics/oss/jackson/module/jsonld/util/AnnotationsUtils.java
// public abstract class AnnotationsUtils {
// 
//     public static boolean isAnnotationPresent(Class<?> type, Class<? extends Annotation> annotationType) {
//         return isAnnotationPresent(type, annotationType, new ArrayList<>());
//     }
// 
//     protected static boolean isAnnotationPresent(Class<?> type, Class<? extends Annotation> annotationType, List<Class<?>> ignore) {
//         if(type.isAnnotationPresent(annotationType)) {
//             return true;
//         }
//         if(type.getAnnotations().length == 0) {
//             return false;
//         }
//         for(Annotation a : type.getAnnotations()) {
//             if(!ignore.contains(a.annotationType())) {
//                 ignore.add(type);
//                 if(isAnnotationPresent(a.annotationType(), annotationType, ignore)) {
//                     return true;
//                 }
//             }
//         }
//         return false;
//     }
// 
// }
// 
// Path: src/main/java/ioinformarics/oss/jackson/module/jsonld/util/JsonUtils.java
// public abstract class JsonUtils {
// 
//     public static JsonNode merge(JsonNode mainNode, JsonNode updateNode) {
// 
//         Iterator<String> fieldNames = updateNode.fieldNames();
//         while (fieldNames.hasNext()) {
// 
//             String fieldName = fieldNames.next();
//             JsonNode jsonNode = mainNode.get(fieldName);
//             // if field exists and is an embedded object
//             if (jsonNode != null && jsonNode.isObject()) {
//                 merge(jsonNode, updateNode.get(fieldName));
//             }
//             else {
//                 if (mainNode instanceof ObjectNode) {
//                     // Overwrite field
//                     JsonNode value = updateNode.get(fieldName);
//                     ((ObjectNode) mainNode).set(fieldName, value);
//                 }
//             }
// 
//         }
// 
//         return mainNode;
//     }
// }
// 
// Path: src/main/java/ioinformarics/oss/jackson/module/jsonld/util/JsonldResourceUtils.java
// public abstract class JsonldResourceUtils {
// 
//     public static Optional<ObjectNode> getContext(Object scopedObj) {
//         return JsonldContextFactory.fromAnnotations(scopedObj);
//     }
// 
//     public static Optional<String> dynamicTypeLookup(Class<?> objType){
//         Optional<String> typeFromAnnotation = Optional.ofNullable(objType.getAnnotation(JsonldType.class))
//                     .map(JsonldType::value);
//         return typeFromAnnotation.isPresent() ? typeFromAnnotation : typeFromJavaClass(objType);
//     }
// 
//     private static Optional<String> typeFromJavaClass(Class<?> objType) {
//         return Optional.ofNullable(objType.getAnnotation(JsonldTypeFromJavaClass.class))
//                 .map((t) -> {
//                     String prefix = t.namespace();
//                     if(prefix.equals(AnnotationConstants.UNASSIGNED)) {
//                         prefix = t.namespacePrefix().equals(AnnotationConstants.UNASSIGNED) ? "" : t.namespacePrefix() + ":";
//                     }
//                     return prefix + objType.getSimpleName();
// 
//                 });
//     }
// }


import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
import ioinformarics.oss.jackson.module.jsonld.annotation.*;
import ioinformarics.oss.jackson.module.jsonld.util.AnnotationsUtils;
import ioinformarics.oss.jackson.module.jsonld.util.JsonUtils;
import ioinformarics.oss.jackson.module.jsonld.util.JsonldResourceUtils;
import org.apache.commons.lang3.ClassUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Stream;
package ioinformarics.oss.jackson.module.jsonld;


/**
 * @author Alexander De Leon
 */
public class JsonldContextFactory {

    public static ObjectNode fromPackage(String packageName) {
        ObjectNode generatedContext = JsonNodeFactory.withExactBigDecimals(true).objectNode();
        FastClasspathScanner scanner = new FastClasspathScanner(packageName);
        scanner.matchAllStandardClasses((clazz) -> {
            if(!Modifier.isAbstract(clazz.getModifiers()) && AnnotationsUtils.isAnnotationPresent(clazz, JsonldTypeFromJavaClass.class)) {
                Optional<String> type = JsonldResourceUtils.dynamicTypeLookup(clazz);
                type.ifPresent(t ->generatedContext.set(clazz.getSimpleName(), TextNode.valueOf(t)));
            }
// Path: src/main/java/ioinformarics/oss/jackson/module/jsonld/util/AnnotationsUtils.java
// public abstract class AnnotationsUtils {
// 
//     public static boolean isAnnotationPresent(Class<?> type, Class<? extends Annotation> annotationType) {
//         return isAnnotationPresent(type, annotationType, new ArrayList<>());
//     }
// 
//     protected static boolean isAnnotationPresent(Class<?> type, Class<? extends Annotation> annotationType, List<Class<?>> ignore) {
//         if(type.isAnnotationPresent(annotationType)) {
//             return true;
//         }
//         if(type.getAnnotations().length == 0) {
//             return false;
//         }
//         for(Annotation a : type.getAnnotations()) {
//             if(!ignore.contains(a.annotationType())) {
//                 ignore.add(type);
//                 if(isAnnotationPresent(a.annotationType(), annotationType, ignore)) {
//                     return true;
//                 }
//             }
//         }
//         return false;
//     }
// 
// }
// 
// Path: src/main/java/ioinformarics/oss/jackson/module/jsonld/util/JsonUtils.java
// public abstract class JsonUtils {
// 
//     public static JsonNode merge(JsonNode mainNode, JsonNode updateNode) {
// 
//         Iterator<String> fieldNames = updateNode.fieldNames();
//         while (fieldNames.hasNext()) {
// 
//             String fieldName = fieldNames.next();
//             JsonNode jsonNode = mainNode.get(fieldName);
//             // if field exists and is an embedded object
//             if (jsonNode != null && jsonNode.isObject()) {
//                 merge(jsonNode, updateNode.get(fieldName));
//             }
//             else {
//                 if (mainNode instanceof ObjectNode) {
//                     // Overwrite field
//                     JsonNode value = updateNode.get(fieldName);
//                     ((ObjectNode) mainNode).set(fieldName, value);
//                 }
//             }
// 
//         }
// 
//         return mainNode;
//     }
// }
// 
// Path: src/main/java/ioinformarics/oss/jackson/module/jsonld/util/JsonldResourceUtils.java
// public abstract class JsonldResourceUtils {
// 
//     public static Optional<ObjectNode> getContext(Object scopedObj) {
//         return JsonldContextFactory.fromAnnotations(scopedObj);
//     }
// 
//     public static Optional<String> dynamicTypeLookup(Class<?> objType){
//         Optional<String> typeFromAnnotation = Optional.ofNullable(objType.getAnnotation(JsonldType.class))
//                     .map(JsonldType::value);
//         return typeFromAnnotation.isPresent() ? typeFromAnnotation : typeFromJavaClass(objType);
//     }
// 
//     private static Optional<String> typeFromJavaClass(Class<?> objType) {
//         return Optional.ofNullable(objType.getAnnotation(JsonldTypeFromJavaClass.class))
//                 .map((t) -> {
//                     String prefix = t.namespace();
//                     if(prefix.equals(AnnotationConstants.UNASSIGNED)) {
//                         prefix = t.namespacePrefix().equals(AnnotationConstants.UNASSIGNED) ? "" : t.namespacePrefix() + ":";
//                     }
//                     return prefix + objType.getSimpleName();
// 
//                 });
//     }
// }


// Path: src/main/java/ioinformarics/oss/jackson/module/jsonld/JsonldContextFactory.java
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
import ioinformarics.oss.jackson.module.jsonld.annotation.*;
import ioinformarics.oss.jackson.module.jsonld.util.AnnotationsUtils;
import ioinformarics.oss.jackson.module.jsonld.util.JsonUtils;
import ioinformarics.oss.jackson.module.jsonld.util.JsonldResourceUtils;
import org.apache.commons.lang3.ClassUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Stream;

package ioinformarics.oss.jackson.module.jsonld;


/**
 * @author Alexander De Leon
 */
public class JsonldContextFactory {

    public static ObjectNode fromPackage(String packageName) {
        ObjectNode generatedContext = JsonNodeFactory.withExactBigDecimals(true).objectNode();
        FastClasspathScanner scanner = new FastClasspathScanner(packageName);
        scanner.matchAllStandardClasses((clazz) -> {
            if(!Modifier.isAbstract(clazz.getModifiers()) && AnnotationsUtils.isAnnotationPresent(clazz, JsonldTypeFromJavaClass.class)) {
                Optional<String> type = JsonldResourceUtils.dynamicTypeLookup(clazz);
                type.ifPresent(t ->generatedContext.set(clazz.getSimpleName(), TextNode.valueOf(t)));
            }
            if(AnnotationsUtils.isAnnotationPresent(clazz, ioinformarics.oss.jackson.module.jsonld.annotation.JsonldResource.class)) {