io.sundr.dsl.internal.graph.functions.Nodes Maven / Gradle / Ivy
/*
* Copyright 2016 The original authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.sundr.dsl.internal.graph.functions;
import io.sundr.Function;
import io.sundr.builder.TypedVisitor;
import io.sundr.codegen.CodegenContext;
import io.sundr.codegen.model.ClassRef;
import io.sundr.codegen.model.Kind;
import io.sundr.codegen.model.Method;
import io.sundr.codegen.model.TypeDef;
import io.sundr.codegen.model.TypeDefBuilder;
import io.sundr.dsl.internal.graph.Node;
import io.sundr.dsl.internal.graph.NodeContext;
import io.sundr.dsl.internal.processor.DslContextManager;
import io.sundr.dsl.internal.type.functions.Combine;
import io.sundr.dsl.internal.type.functions.Generics;
import io.sundr.dsl.internal.type.functions.Generify;
import io.sundr.dsl.internal.utils.GraphUtils;
import io.sundr.dsl.internal.utils.TypeDefUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import static io.sundr.dsl.internal.Constants.BEGIN_SCOPE;
import static io.sundr.dsl.internal.Constants.CARDINALITY_MULTIPLE;
import static io.sundr.dsl.internal.Constants.END_SCOPE;
import static io.sundr.dsl.internal.Constants.IS_GENERATED;
import static io.sundr.dsl.internal.Constants.KEYWORDS;
import static io.sundr.dsl.internal.Constants.SCOPE_SUFFIX;
import static io.sundr.dsl.internal.utils.GraphUtils.exclusion;
import static io.sundr.dsl.internal.utils.GraphUtils.isSatisfied;
import static io.sundr.dsl.internal.utils.TypeDefUtils.isBeginScope;
import static io.sundr.dsl.internal.utils.TypeDefUtils.isEndScope;
import static io.sundr.dsl.internal.utils.TypeDefUtils.isEntryPoint;
import static io.sundr.dsl.internal.utils.TypeDefUtils.isTerminal;
import static io.sundr.dsl.internal.utils.TypeDefUtils.isTransition;
public class Nodes {
/**
* {@link Function} that accepts a set of {@link TypeDef} and creates a the transition graph.
* The graph is structured as a set of Trees (tree-like to be more accurate as there might be circles).
*/
public static final Function, Set>> TO_GRAPH = new Function, Set>>() {
public Set> apply(Set clazzes) {
Set> nodes = new LinkedHashSet>();
Set all = new LinkedHashSet(clazzes);
for (TypeDef clazz : clazzes) {
if (isEntryPoint(clazz)) {
nodes.add(TO_TREE.apply(NodeContext.builder()
.withItem(clazz)
.withAll(all)
.build()));
}
}
return nodes;
}
};
public static final Function> TO_TREE = new Function>() {
public Node apply(NodeContext context) {
Set> nextVertices = new LinkedHashSet>();
//visited and path are the same only in the first iteration. see bellow:
Set visited = new LinkedHashSet(context.getVisited());
List nextCandidates = new ArrayList(TO_NEXT.apply(context));
Collections.sort(nextCandidates, new CandidateComparator(context));
for (TypeDef next : nextCandidates) {
NodeContext nextContext = context.contextOfChild(next)
.addToVisited(visited)
.addToVisited(next)
.build();
Node subGraph = TO_TREE.apply(nextContext);
//Let's keep track of types used so far in the loop so that we avoid using the same types, in different branches of the tree:
//This is required so that we avoid extending the same generic interface with different parameters.
visited.add(subGraph.getItem());
if (subGraph.getTransitions().size() > 0 || isTerminal(subGraph.getItem()) || isEndScope(subGraph.getItem())) {
nextVertices.add(subGraph);
}
}
return DslContextManager.getContext().getNodeRepository().getOrCreateNode(context.getItem(), nextVertices);
}
};
/**
* {@Function} that accepts a {@link NodeContext} and finds which classes can follow next.
*/
public static final Function> TO_NEXT = new Function>() {
public Set apply(NodeContext context) {
Set result = new LinkedHashSet();
Boolean inScope = !context.getActiveScopes().isEmpty();
if (inScope && isEndScope(context.getItem()) || isTerminal(context.getItem())) {
return result;
}
List currentPath = context.getPathTypes();
currentPath.add(context.getItem());
for (TypeDef candidate : exclusion(context.getAll(), context.getVisitedTypes())) {
if (!isEntryPoint(candidate) && isSatisfied(candidate, currentPath)) {
result.add(candidate);
}
}
result.remove(context.getItem());
return result;
}
};
public static final Function, TypeDef> TO_ROOT = new Function, TypeDef>() {
public TypeDef apply(Node item) {
List interfaces = new ArrayList();
for (Node child : item.getTransitions()) {
ClassRef transitionInterface = TO_TRANSITION.apply(child);
interfaces.add(transitionInterface);
CodegenContext.getContext().getDefinitionRepository().register(child.getItem(), IS_GENERATED);
}
TypeDef rootType = new TypeDefBuilder(item.getItem())
.withExtendsList(interfaces)
.withParameters()
.withMethods()
.build();
return new TypeDefBuilder(Generics.UNWRAP.apply(rootType))
.withMethods(new ArrayList())
.build();
}
};
public static final Function, ClassRef> TO_TRANSITION = new Function, ClassRef>() {
public ClassRef apply(Node current) {
if (current.getTransitions().isEmpty()) {
return current.getItem().toInternalReference();
} else {
TypeDef clazz = current.getItem();
Set toCombine = new LinkedHashSet();
for (Node v : current.getTransitions()) {
toCombine.add(apply(v));
}
ClassRef nextClazz = toCombine.size() == 1
? toCombine.iterator().next()
: Combine.TYPEREFS.apply(Generify.CLASSREFS.apply(toCombine)).toInternalReference();
if (TypeDefUtils.isCardinalityMultiple(clazz)) {
//1st pass create the self ref
final ClassRef selfRef = transition(clazz, nextClazz);
Set toReCombine = new LinkedHashSet(toCombine);
toReCombine.add(selfRef);
TypeDef reCombinedType = Combine.TYPEREFS.apply(toReCombine);
final ClassRef reCombinedRef = reCombinedType.toInternalReference();
reCombinedType = new TypeDefBuilder(reCombinedType).accept(new TypedVisitor() {
public void visit(TypeDefBuilder builder) {
List updatedInterfaces = new ArrayList();
for (ClassRef interfaceRef : builder.getExtendsList()) {
if (interfaceRef.equals(selfRef)) {
updatedInterfaces.add(selfRef.getDefinition().toReference(reCombinedRef));
} else {
updatedInterfaces.add(interfaceRef);
}
}
builder.withExtendsList(updatedInterfaces);
}
}).build();
ClassRef reCombined = reCombinedType.toInternalReference();
//2nd pass recreate the combination
ClassRef updatedSelfRef = transition(clazz, reCombined);
toReCombine = new LinkedHashSet(toCombine);
toReCombine.add(updatedSelfRef);
reCombined = Combine.TYPEREFS.apply(toReCombine).toInternalReference();
DslContextManager.getContext().getDefinitionRepository().register(reCombinedType, IS_GENERATED);
DslContextManager.getContext().getDefinitionRepository().register(nextClazz.getDefinition(), IS_GENERATED);
return transition(clazz, reCombined);
} else {
//If we have a couple of classes to combine that are non-multiple
// we may end up with intermediate garbage in the registry, which are masking the real thing
if (!isTransition(nextClazz)
//&&
//DslContextManager.getContext().getDefinitionRepository().getDefinition(nextClazz.getDefinition().getFullyQualifiedName()) == null
) {
DslContextManager.getContext().getDefinitionRepository().register(nextClazz.getDefinition(), IS_GENERATED);
}
return transition(clazz, nextClazz);
}
}
}
public ClassRef transition(TypeDef from, ClassRef to) {
return from.toReference(to);
}
};
public static final Function, Node> TO_UNCYCLIC = new Function, Node>() {
public Node apply(Node node) {
visit(node, new LinkedHashSet>());
return node;
}
private boolean visit(Node node, Set> visited) {
if (visited.add(node)) {
for (Node child : new LinkedHashSet>(node.getTransitions())) {
Set> branchNodes = new LinkedHashSet>(visited);
if (!visit(child, branchNodes)) {
node.getTransitions().remove(child);
}
}
return true;
} else {
return false;
}
}
};
public static final Function> TO_UNWRAPPED = new Function>() {
public Node apply(NodeContext ctx) {
Node current = DslContextManager.getContext().getNodeRepository().get(ctx.getItem());
Set> next = new LinkedHashSet>();
for (Node candidate : current.getTransitions()) {
List currentPath = ctx.getPathTypes();
currentPath.add(ctx.getItem());
if (GraphUtils.isSatisfied(candidate.getItem(), currentPath)) {
Node subGraph = apply(ctx.contextOfChild(candidate.getItem()).build());
if (subGraph.getTransitions().size() > 0 || isTerminal(subGraph.getItem()) || isEndScope(subGraph.getItem())) {
next.add(subGraph);
}
}
}
return DslContextManager.getContext().getNodeRepository().createNode(ctx.getItem(), next);
}
};
public static Function, Set> TO_SCOPE = new Function, Set>() {
public Set apply(Set clazzes) {
Set result = new LinkedHashSet(clazzes);
Set all = new LinkedHashSet(clazzes);
for (TypeDef clazz : clazzes) {
if (isBeginScope(clazz)) {
Boolean multiple = TypeDefUtils.isCardinalityMultiple(clazz);
TypeDef current;
if (multiple) {
current = new TypeDefBuilder(clazz)
.addToAttributes(CARDINALITY_MULTIPLE, false)
.build();
all.remove(clazz);
all.add(current);
} else {
current = clazz;
}
Node node = TO_TREE.apply(NodeContext.builder()
.withItem(current)
.withAll(all)
.build());
Set scopeClasses = scopeClasses(node);
for (TypeDef scopeClass : scopeClasses) {
DslContextManager.getContext().getDefinitionRepository().register(scopeClass, IS_GENERATED);
}
ClassRef scopeInterface = TO_TRANSITION.apply(node);
result.removeAll(scopeClasses);
result.add(new TypeDefBuilder(clazz)
.withKind(Kind.INTERFACE)
.withPackageName(scopeInterface.getDefinition().getPackageName())
.withName(scopeInterface.getDefinition().getName() + SCOPE_SUFFIX)
.withExtendsList(scopeInterface)
.withMethods()
.addToAttributes(CARDINALITY_MULTIPLE, multiple)
.addToAttributes(KEYWORDS, scopeKeywords(scopeClasses))
.accept(new TypedVisitor() {
public void visit(TypeDefBuilder builder) {
builder.getAttributes().remove(BEGIN_SCOPE);
builder.getAttributes().remove(END_SCOPE);
}
})
.build());
}
}
return result;
}
public Set scopeClasses(Node node) {
Set result = new LinkedHashSet();
result.add(node.getItem());
for (Node transition : node.getTransitions()) {
result.addAll(scopeClasses(transition));
}
return result;
}
public Set scopeKeywords(Collection clazzes) {
Set result = new LinkedHashSet();
for (TypeDef clazz : clazzes) {
Set keywords = (Set) clazz.getAttributes().get(KEYWORDS);
result.addAll(keywords != null ? keywords : Collections.emptySet());
}
return result;
}
};
private static class CandidateComparator implements Comparator {
private final NodeContext nodeContext;
private CandidateComparator(NodeContext nodeContext) {
this.nodeContext = nodeContext;
}
public int compare(TypeDef left, TypeDef right) {
Set leftSet = TO_NEXT.apply(nodeContext.contextOfChild(left).addToVisited(left).build());
Set rightSet = TO_NEXT.apply(nodeContext.contextOfChild(right).addToVisited(right).build());
if (leftSet.contains(right) && rightSet.contains(left)) {
return 0;
} else if (leftSet.contains(right)) {
return -1;
} else if (rightSet.contains(left)) {
return 1;
}
return 0;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy