Search in sources :

Example 1 with IReferenceNode

use of io.apicurio.datamodels.core.models.IReferenceNode in project apicurio-data-models by Apicurio.

the class Dereferencer method dereference.

/**
 * Execute the algorithm.
 *
 * @return dereferenced clone of the source document.
 * @throws java.lang.RuntimeException if strict and some references could not be dereferenced
 */
public Document dereference() {
    Document clone = Library.cloneDocument(source);
    IReferenceManipulationStrategy strategy = null;
    switch(clone.getDocumentType()) {
        case asyncapi2:
            strategy = new Aai20IReferenceManipulationStrategy();
            break;
        case openapi2:
            strategy = new Oas20IReferenceManipulationStrategy();
            break;
        case openapi3:
            strategy = new Oas30IReferenceManipulationStrategy();
            break;
        default:
            throw new RuntimeException("Unknown document type: " + clone.getDocumentType());
    }
    // Keeps the nodes waiting to be processed (BFS-style)
    Queue<Context> processQueue = new LinkedList<>();
    // start with the whole model
    processQueue.add(new Context(null, clone));
    // Prevents recursive loops
    Map<String, String> resolvedToLocalMap = new HashMap<>();
    for (String ref : strategy.getExistingLocalComponents(clone).keySet()) {
        resolvedToLocalMap.put(ref, ref);
    }
    while (!processQueue.isEmpty()) {
        Context item = processQueue.remove();
        // Local components
        Map<String, Node> localComponents = strategy.getExistingLocalComponents(clone);
        // Collect all reference objects in the processed node
        ReferenceCollectionVisitor rcv = new ReferenceCollectionVisitor();
        VisitorUtil.visitTree(item.node, rcv, TraverserDirection.down);
        List<IReferenceNode> referencedNodes = rcv.getReferencedNodes();
        // For each reference node ...
        for (IReferenceNode node : referencedNodes) {
            // skip if already local, this makes sense if we're initially processing the whole Document
            if (item.parentRef == null && localComponents.containsKey(node.getReference()))
                continue;
            // Reference to be resolved
            Reference originalRef = new Reference(node.getReference());
            // Attempt to resolve
            if (item.parentRef != null && originalRef.isRelative()) {
                originalRef = originalRef.withAbsoluteFrom(new Reference(item.parentRef));
            }
            // to avoid cycles
            if (resolvedToLocalMap.containsKey(originalRef.getRef())) {
                node.setReference(resolvedToLocalMap.get(originalRef.getRef()));
                continue;
            }
            Node resolved = resolver.resolveRef(originalRef.getRef(), (Node) node);
            // if null keep the reference in an 'unresolvable' set to decide later
            if (resolved == null) {
                unresolvable.add(node.getReference());
            } else {
                IReferenceManipulationStrategy.ReferenceAndNode localRef = null;
                String name = originalRef.getName();
                // Remove the reference node so its name can be reused if possible.
                boolean nameReused = false;
                if (name.equals(strategy.getComponentName(clone, resolved))) {
                    nameReused = strategy.removeComponent(clone, name);
                }
                // repeat in case of name conflict
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
                    if (i > 0)
                        name = originalRef.getName() + i;
                    try {
                        localRef = strategy.attachAsComponent(clone, name, resolved);
                        break;
                    } catch (IllegalArgumentException ex) {
                        // Assert we should be reusing the name
                        if (nameReused)
                            throw new IllegalStateException("Assertion error: " + "Component with name '" + name + "' should be reused, but can't be attached.");
                    // TODO maybe avoid exceptions?
                    }
                }
                if (localRef == null) {
                    unresolvable.add(node.getReference());
                } else {
                    // rename the original reference
                    if (!nameReused) {
                        node.setReference(localRef.getRef());
                    }
                    // add resolved node to processing queue
                    processQueue.add(new Context(originalRef.getRef(), localRef.getNode()));
                    // remember, to prevent cycles
                    resolvedToLocalMap.put(originalRef.getRef(), localRef.getRef());
                }
            }
        }
    }
    // we're done, if strict, throw
    if (unresolvable.size() != 0 && strict)
        throw new RuntimeException("Could not resolve some references: " + unresolvable);
    return clone;
}
Also used : IReferenceNode(io.apicurio.datamodels.core.models.IReferenceNode) HashMap(java.util.HashMap) IReferenceNode(io.apicurio.datamodels.core.models.IReferenceNode) Node(io.apicurio.datamodels.core.models.Node) Document(io.apicurio.datamodels.core.models.Document) LinkedList(java.util.LinkedList)

Aggregations

Document (io.apicurio.datamodels.core.models.Document)1 IReferenceNode (io.apicurio.datamodels.core.models.IReferenceNode)1 Node (io.apicurio.datamodels.core.models.Node)1 HashMap (java.util.HashMap)1 LinkedList (java.util.LinkedList)1