org.babyfish.jimmer.client.generator.Context Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jimmer-client Show documentation
Show all versions of jimmer-client Show documentation
A revolutionary ORM framework for both java and kotlin
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);
}
}
}