integration/cauldron/ConceptTreeGenerator.js

/**
 *  ConceptDecorator - decorates Concept nodes in TreeBrowser trees
 *  
 *  This code is licensed under the MIT License (MIT).
 *  
 *  Copyright 2020, 2021, 2022 Rolf Bagge, Janus B. Kristensen, CAVI,
 *  Center for Advanced Visualization and Interaction, Aarhus University
 *  
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the “Software”), to deal
 *  in the Software without restriction, including without limitation the rights 
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 *  copies of the Software, and to permit persons to whom the Software is 
 *  furnished to do so, subject to the following conditions:
 *  
 *  The above copyright notice and this permission notice shall be included in 
 *  all copies or substantial portions of the Software.
 *  
 *  THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
 *  THE SOFTWARE.
 *  
 */

/**
 *
 */
class ConceptTreeGenerator extends TreeGenerator {
    constructor(parentNode) {
        super();

        this.rootNode = new TreeNode({
            type: "ConceptRootNode",
            context: null
        });
        TreeGenerator.decorateNode(this.rootNode);
        parentNode.addNode(this.rootNode);
    }
    
    onAddDatastore(datastore){
        let activeElement = document.activeElement;

        let self = this;
        let datastoreNode = new TreeNode({
            type: "DatastoreNode",
            lookupKey: datastore.name,
            context: datastore
        });
        TreeGenerator.decorateNode(datastoreNode);
        this.rootNode.addNode(datastoreNode);
        this.rootNode.unfold();
        
        datastore.registerDestroyCallback(()=>{
            self.destroyNode(datastoreNode);
        });
        
        // Concepts
        datastore.registerConceptAddedCallback((concept)=>{
            datastoreNode.unfold();
            
            let conceptNode = new TreeNode({
                type: "ConceptNode",
                lookupKey: concept.name,
                context: concept
            });
            
            // Instances
            let instanceAddedCallback = datastore.registerConceptInstanceAddedCallback(concept, (uuid)=>{
                let node = self.addInstanceNode(datastore, conceptNode, concept, uuid);
                let instanceRemovedCallback = datastore.registerConceptInstanceRemovedCallback(concept, (removedUUID)=>{
                    if (removedUUID===uuid){
                        self.destroyNode(node);
                    }
                });
                node.cleanup = [()=>{
                    datastore.removeConceptInstanceRemovedCallback(concept, instanceRemovedCallback);
                }];
            });
            
            TreeGenerator.decorateNode(conceptNode);
            datastoreNode.addNode(conceptNode);
        });

        // STUB: Concepts cannot be removed right now but should be cleaned up here
        // datastore.removeConceptInstanceAddedCallback(concept, instanceAddedCallback);
        
    }    
    
    destroyNode(node){
        node.parentNode.removeNode(node);
        
        // Run cleanup
        if (node.cleanup){
            for (let entry of node.cleanup){
                entry();
            }
        }
        
        // Destroy children
        for (let child of Array.from(node.childNodes)){ // copy to avoid concurrent mods
            this.destroyNode(child);
        }
    }
    
    addInstanceNode(datastore, parentNode, concept, uuid){
        let instanceNode = new TreeNode({
            type: "ConceptInstanceNode",
            lookupKey: uuid,
            context: {concept, uuid, datastore}
        });    
        TreeGenerator.decorateNode(instanceNode);
        parentNode.addNode(instanceNode);
        return instanceNode;
    }
}


EventSystem.registerEventCallback("Cauldron.TreeBrowserSpawned", ({detail: {root: rootNode}})=>{
    let generator = new ConceptTreeGenerator(rootNode);
    EventSystem.triggerEvent("Varv.ConceptTreeGeneratorSpawned", generator);
    window.ConceptTreeGenerator.instances.push(generator);    
});

window.ConceptTreeGenerator = ConceptTreeGenerator;
window.ConceptTreeGenerator.instances = [];