All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.babyfish.jimmer.client.generator.Context Maven / Gradle / Ivy

There is a newer version: 0.8.177
Show newest version
package org.babyfish.jimmer.client.generator;

import org.babyfish.jimmer.client.generator.ts.TsCodeWriter;
import org.babyfish.jimmer.client.meta.*;

import java.io.OutputStream;
import java.util.*;
import java.util.function.Function;

public class Context {

    private static final Comparator SERVICE_COMPARATOR =
            Comparator.comparing(a -> a.getJavaType().getName());

    private static final Comparator DTO_COMPARATOR =
            Comparator
                    .comparing(Context::totalPropCount)
                    .thenComparing((ImmutableObjectType it) -> it.getJavaType().getName())
                    .thenComparing((ImmutableObjectType it) -> {
                        FetchByInfo info = it.getFetchByInfo();
                        return info != null ? info.getOwnerType().getName() : "";
                    })
                    .thenComparing((ImmutableObjectType it) -> {
                        FetchByInfo info = it.getFetchByInfo();
                        return info != null ? info.getConstant() : "";
                    });

    private final OutputStream out;

    private final File moduleFile;

    private final File moduleErrorsFile;

    private final String indent;

    private final Map, List> dtoMap;

    private final Map, StaticObjectType> genericTypeMap;

    private final Map operationNameMap;

    private final NavigableMap serviceFileMap;

    private final Map typeFileMap;

    private final Namespace> fetchByOwnerNamespace;

    private final Namespace> dtoPrefixNamespace;

    public Context(Metadata metadata, OutputStream out, String moduleName, int indent) {
        this.out = out;
        this.moduleFile = new File("", moduleName);
        this.moduleErrorsFile = new File("", moduleName + "Errors");
        if (indent < 2) {
            throw new IllegalArgumentException("indent cannot be less than 2");
        }
        StringBuilder builder = new StringBuilder();
        for (int i = indent; i > 0; --i) {
            builder.append(' ');
        }
        this.indent = builder.toString();
        this.genericTypeMap = metadata.getGenericTypes();

        VisitorImpl impl = new VisitorImpl();
        for (Service service : metadata.getServices().values()) {
            service.accept(impl);
        }
        for (StaticObjectType genericType : metadata.getGenericTypes().values()) {
            genericType.accept(impl);
        }

        operationNameMap = impl.operationNamespace.getNameMap();

        Map, List> dtoMap = new HashMap<>();
        for (ImmutableObjectType immutableObjectType : metadata.getFetchedImmutableObjectTypes().values()) {
            dtoMap
                    .computeIfAbsent(immutableObjectType.getJavaType(), it -> new ArrayList<>())
                    .add(immutableObjectType);
        }
        for (ImmutableObjectType immutableObjectType : metadata.getViewImmutableObjectTypes().values()) {
            dtoMap
                    .computeIfAbsent(immutableObjectType.getJavaType(), it -> new ArrayList<>())
                    .add(immutableObjectType);
        }
        for (Map.Entry, List> e : dtoMap.entrySet()) {
            List types = e.getValue();
            types.sort(DTO_COMPARATOR);
            e.setValue(Collections.unmodifiableList(types));
        }
        this.dtoMap = Collections.unmodifiableMap(dtoMap);

        NavigableMap map = new TreeMap<>(SERVICE_COMPARATOR);
        map.putAll(impl.serviceFileManager.getFileMap());
        serviceFileMap = Collections.unmodifiableNavigableMap(map);

        typeFileMap = impl.typeFileManager.getFileMap();

        fetchByOwnerNamespace = impl.fetchByOwnerNamespace;

        dtoPrefixNamespace = new Namespace<>(clazz -> staticTypeName(clazz) + "Dto");
    }

    public OutputStream getOutputStream() {
        return out;
    }

    public File getModuleFile() {
        return moduleFile;
    }

    public File getModuleErrorsFile() {
        return moduleErrorsFile;
    }

    public String getIndent() {
        return indent;
    }

    public File getFile(Service service) {
        if (service == null) {
            throw new IllegalArgumentException("service cannot be null");
        }
        return serviceFileMap.get(service);
    }

    public File getFile(Type type) {
        return typeFileMap.get(rawType(type));
    }

    public String getOperationName(Operation operation) {
        return operationNameMap.get(operation);
    }

    public Map getServiceFileMap() {
        return serviceFileMap;
    }

    public Iterable> getTypeFilePairs() {
        return () -> typeFileMap.entrySet().iterator();
    }

    public Map, List> getDtoMap() {
        return dtoMap;
    }

    public String getDtoPrefix(Class rawType) {
        return dtoPrefixNamespace.get(rawType);
    }

    public String getDtoSuffix(ImmutableObjectType type) {
        FetchByInfo fetchByInfo = type.getFetchByInfo();
        if (fetchByInfo != null) {
            return fetchByOwnerNamespace.get(fetchByInfo.getOwnerType()) + "/" + fetchByInfo.getConstant();
        }
        if (type.getCategory() == ImmutableObjectType.Category.VIEW) {
            return "DEFAULT";
        }
        return null;
    }

    private Type rawType(Type type) {
        if (type instanceof StaticObjectType) {
            StaticObjectType staticObjectType = (StaticObjectType) type;
            if (!staticObjectType.getTypeArguments().isEmpty()) {
                return genericTypeMap.get(staticObjectType.getJavaType());
            }
        }
        return type;
    }

    private static int totalPropCount(ImmutableObjectType type) {
        int count = type.getProperties().size();
        for (Property prop : type.getProperties().values()) {
            if (prop.getType() instanceof ImmutableObjectType) {
                count += totalPropCount((ImmutableObjectType) prop.getType());
            }
        }
        return count;
    }

    protected String dynamicTypeName(ImmutableObjectType type) {
        return type.getJavaType().getSimpleName();
    }

    private String staticTypeName(Class type) {
        Class declaringClass = type.getDeclaringClass();
        if (declaringClass == null) {
            return type.getSimpleName();
        }
        return staticTypeName(declaringClass) + nestedTypeSeparator() + type.getSimpleName();
    }

    protected String staticDirName() {
        return "model/static";
    }

    protected String nestedTypeSeparator() {
        return ".";
    }

    private class VisitorImpl implements Visitor {

        private Set visitedStaticTypes = new HashSet<>();

        private String serviceName;

        final Namespace serviceNamespace = new Namespace<>(
                service -> staticTypeName(service.getJavaType())
        );

        final Namespace operationNamespace = new Namespace<>(
                operation -> serviceName + "::" + operation.getName(),
                name -> name.substring(name.indexOf("::") + 2)
        );

        final Namespace typeNamespace = new Namespace<>(
                type -> {
                    if (type instanceof SimpleType) {
                        return TsCodeWriter.SIMPLE_TYPE_NAMES.get(((SimpleType)type).getJavaType());
                    }
                    if (type instanceof ImmutableObjectType) {
                        return dynamicTypeName((ImmutableObjectType)type);
                    } else if (type instanceof StaticObjectType) {
                        Class javaType = ((StaticObjectType)type).getJavaType();
                        return staticTypeName(javaType);
                    } else if (type instanceof EnumType) {
                        return staticTypeName(((EnumType)type).getJavaType());
                    }
                    return null;
                }
        );

        final FileManager serviceFileManager = new FileManager<>(
                service -> "services",
                serviceNamespace
        );

        final FileManager typeFileManager = new FileManager<>(
                type -> {
                    if (!type.hasDefinition()) {
                        return null;
                    }
                    if (type instanceof ImmutableObjectType) {
                        return "model/entities";
                    } else if (type instanceof StaticObjectType) {
                        return staticDirName();
                    } else if (type instanceof EnumType) {
                        return "model/enums";
                    }
                    return null;
                },
                typeNamespace
        );

        final Namespace> fetchByOwnerNamespace = new Namespace<>(Context.this::staticTypeName);

        private final Map, List> dtoMap = new HashMap<>();

        @Override
        public void visitingService(Service service) {
            serviceName = serviceFileManager.add(service).getName();
            // Keep some fetcher owner names are same with service names
            fetchByOwnerNamespace.get(service.getClass());
        }

        @Override
        public void visitedService(Service service) {
            serviceName = null;
        }

        @Override
        public void visitingOperation(Operation operation) {
            operationNamespace.get(operation);
        }

        @Override
        public boolean isTypeVisitable(Type type) {
            return !typeFileManager.getFileMap().containsKey(type);
        }

        @Override
        public void visitImmutableObjectType(ImmutableObjectType immutableObjectType) {
            if (typeFileManager.add(immutableObjectType) == null) {
                dtoMap
                        .computeIfAbsent(immutableObjectType.getJavaType(), it -> new ArrayList<>())
                        .add(immutableObjectType);
            }
        }

        @Override
        public boolean visitStaticObjectType(StaticObjectType staticObjectType) {
            if (visitedStaticTypes.add(staticObjectType)) {
                while (staticObjectType.getDeclaringObjectType() != null) {
                    staticObjectType = staticObjectType.getDeclaringObjectType();
                }
                typeFileManager.add(staticObjectType);
                return true;
            }
            return false;
        }

        @Override
        public void visitEnumType(EnumType enumType) {
            typeFileManager.add(enumType);
        }
    }

    private static class Namespace {

        private final Map nameMap = new IdentityHashMap<>();

        private final Map nameCountMap = new HashMap<>();

        private final Function initializer;

        private final Function terminator;

        Namespace(Function initializer) {
            this.initializer = initializer;
            this.terminator = null;
        }

        Namespace(Function initializer, Function terminator) {
            this.initializer = initializer;
            this.terminator = terminator;
        }

        public String get(T node) {
            String name = nameMap.get(node);
            if (name == null) {
                name = initializer.apply(node);
                Integer count = nameCountMap.get(name);
                if (count == null) {
                    nameCountMap.put(name, 1);
                } else {
                    count++;
                    nameCountMap.put(name, count);
                    name = name + '_' + count;
                }
                if (terminator != null) {
                    name = terminator.apply(name);
                }
                nameMap.put(node, name);
            }
            return name;
        }

        public Map getNameMap() {
            return Collections.unmodifiableMap(nameMap);
        }
    }

    private static class FileManager {

        private final Map fileMap = new IdentityHashMap<>();

        private final Function dirSupplier;

        private final Namespace namespace;

        private FileManager(Function dirSupplier, Namespace namespace) {
            this.dirSupplier = dirSupplier;
            this.namespace = namespace;
        }

        public File add(N node) {
            File file = fileMap.get(node);
            if (file == null && !fileMap.containsKey(node)) {
                String dir = dirSupplier.apply(node);
                if (dir == null) {
                    return null;
                }
                String name = namespace.get(node);
                file = new File(dir, name);
                fileMap.put(node, file);
            }
            return file;
        }

        public Map getFileMap() {
            return Collections.unmodifiableMap(fileMap);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy