org.nuiton.topia.templates.sql.TopiaMetadataEntityPathsBuilder Maven / Gradle / Ivy
package org.nuiton.topia.templates.sql;
/*-
* #%L
* Toolkit :: Templates
* %%
* Copyright (C) 2017 - 2024 Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
* #L%
*/
import com.google.common.collect.ArrayListMultimap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataAssociation;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataEntity;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataEntityPath;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataLink;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataModel;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataModelPaths;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataModelVisitor;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataOneToOneComposition;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataReverseAssociation;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.stream.Stream;
/**
* To build {@link TopiaMetadataEntityPath} for a given type.
*
* Created on 20/08/15.
*
* @author Tony Chemit - [email protected]
*/
public class TopiaMetadataEntityPathsBuilder {
private static final Logger log = LogManager.getLogger(TopiaMetadataEntityPathsBuilder.class);
public static class Visitor extends TopiaMetadataModelVisitor.TopiaMetadataModelVisitorAdapter {
private final ArrayListMultimap pathsBuilder;
private final Deque stack;
private final Deque paths;
public Visitor() {
this.pathsBuilder = ArrayListMultimap.create();
this.paths = new ArrayDeque<>();
this.stack = new ArrayDeque<>();
}
@Override
public void visitEntityStart(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity) {
log.info("Start visit: " + metadataEntity);
stack.offer(metadataEntity);
}
@Override
public void visitEntityEnd(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity) {
TopiaMetadataEntity poll = stack.poll();
log.info("End visit: " + poll);
}
@Override
public void visitOneToOneAssociation(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType) {
if (rejectAssociationType(metadataEntity, propertyName, propertyType)) {
return;
}
registerPath(metadataModel, metadataEntity, propertyName, propertyType);
}
@Override
public void visitOneToManyAssociation(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType) {
if (rejectAssociationType(metadataEntity, propertyName, propertyType)) {
return;
}
registerPath(metadataModel, metadataEntity, propertyName, propertyType);
}
@Override
public void visitManyToManyAssociation(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType) {
if (rejectAssociationType(metadataEntity, propertyName, propertyType)) {
return;
}
registerPath(metadataModel, metadataEntity, propertyName, propertyType);
}
@Override
public void visitManyToOneAssociation(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType) {
if (rejectAssociationType(metadataEntity, propertyName, propertyType)) {
return;
}
registerPath(metadataModel, metadataEntity, propertyName, propertyType);
}
@Override
public void visitReversedAssociation(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType) {
if (rejectReversedAssociationType(metadataEntity, propertyName, propertyType)) {
return;
}
registerPath(metadataModel, metadataEntity, propertyName, propertyType);
}
protected boolean rejectReversedAssociationType(TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType) {
return metadataEntity.isSkipNavigation(propertyName) || rejectType(propertyType);
}
protected boolean rejectAssociationType(TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType) {
return metadataEntity.isSkipNavigation(propertyName) || rejectType(propertyType);
}
protected boolean rejectType(TopiaMetadataEntity propertyType) {
return stack.contains(propertyType) || propertyType.isStandalone();
}
protected void registerPath(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity, String propertyName, TopiaMetadataEntity propertyType) {
log.info(String.format("Start to register new path from link: %s → %s", metadataEntity.getType(), propertyName));
TopiaMetadataLink link;
{
Stream candidates = metadataModel.getReverseOneToManyAssociations(propertyType).stream().filter(l -> l.getTargetPropertyName().equals(propertyName));
link = candidates.filter(l -> l.getOwner().equals(metadataEntity)).findFirst().orElse(null);
}
if (link == null) {
Stream candidates = metadataModel.getReverseOneToOneAssociations(propertyType).stream().filter(l -> l.getTargetPropertyName().equals(propertyName));
link = candidates.filter(l -> l.getOwner().equals(metadataEntity)).findFirst().orElse(null);
}
if (link == null) {
Stream candidates = metadataModel.getReverseAssociations(metadataEntity).stream().filter(l -> l.getTargetPropertyName().equals(propertyName));
link = candidates.filter(l -> l.getOwner().equals(metadataEntity)).findFirst().orElse(null);
}
if (link == null) {
log.error(String.format("Can't find link for %s → %s", metadataEntity.getType(), propertyName));
}
TopiaMetadataEntityPath parentPath = paths.peek();
TopiaMetadataEntityPath newPath = parentPath == null ? TopiaMetadataEntityPath.of(link) : parentPath.resolve(link);
paths.offerFirst(newPath);
try {
propertyType.accept(this, metadataModel);
} finally {
TopiaMetadataEntityPath entityPath = Objects.requireNonNull(paths.poll());
pathsBuilder.put(entityPath.getEnd(), entityPath);
log.warn(String.format("Register new path: %s", entityPath));
}
}
}
public static TopiaMetadataModelPaths forEntryPoint(TopiaMetadataModelPaths all, TopiaMetadataEntity entryPoint) {
TreeMap> entityPathsBuilder = new TreeMap<>();
for (Map.Entry> entry : all.asMap().entrySet()) {
for (TopiaMetadataEntityPath path : entry.getValue()) {
if (Objects.equals(entryPoint, path.getStart())) {
entityPathsBuilder.put(entry.getKey(), List.of(path));
}
}
}
return new TopiaMetadataModelPaths(entityPathsBuilder);
}
public static TopiaMetadataModelPaths forType(TopiaMetadataModelPaths all, TopiaMetadataEntity entryPoint) {
TreeMap> entityPathsBuilder = new TreeMap<>();
for (Map.Entry> entry : all.asMap().entrySet()) {
for (TopiaMetadataEntityPath path : entry.getValue()) {
if (Objects.equals(entryPoint, path.getEnd())) {
continue;
}
List links = new ArrayList<>(path.getLinks());
boolean match = false;
Iterator iterator = links.iterator();
while (iterator.hasNext()) {
TopiaMetadataLink link = iterator.next();
if (Objects.equals(entryPoint, link.getOwner())) {
match = true;
break;
}
iterator.remove();
}
if (match) {
entityPathsBuilder.put(entry.getKey(), List.of(new TopiaMetadataEntityPath(List.copyOf(links))));
}
}
}
return new TopiaMetadataModelPaths(entityPathsBuilder);
}
public static TopiaMetadataModelPaths create(TopiaMetadataModel model) {
TopiaMetadataEntityPathsBuilder builder = new TopiaMetadataEntityPathsBuilder();
TreeMap> entityPathsBuilder = new TreeMap<>();
model.streamWithEntryPoint().forEach(e -> {
TreeMap> build = builder.build(model, e);
entityPathsBuilder.putAll(build);
});
return new TopiaMetadataModelPaths(entityPathsBuilder);
}
public TopiaMetadataEntityPathsBuilder() {
}
public TreeMap> build(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity) {
Visitor v = createVisitor();
v.visitModelStart(metadataModel);
metadataEntity.accept(v, metadataModel);
v.visitModelEnd(metadataModel);
TreeMap> result = new TreeMap<>();
v.pathsBuilder.asMap().forEach((k, c) -> result.put(k, List.copyOf(c)));
return result;
}
protected Visitor createVisitor() {
return new Visitor();
}
}