Skip to content
Snippets Groups Projects
graph.ts 5.45 KiB
Newer Older
  • Learn to ignore specific revisions
  • import ManagedData from "../manageddata";
    import {Link} from "./link";
    import {NodeType} from "./nodetype";
    import {Node} from "./node";
    import {GLOBAL_PARAMS} from "../helper/serializableitem";
    
    import { GraphElement } from "./graphelement";
    
    const GRAPH_PARAMS = [...GLOBAL_PARAMS];
    const GRAPH_DATA_PARAMS = ["nodes", "links", "types"];
    
    type GraphData = { nodes: Node[], links: Link[], types: NodeType[] };
    
    
    export class Graph extends ManagedData {
    
        public data: GraphData;
    
        private nextNodeId: number = 0;
        private nextLinkId: number = 0;
        private nextTypeId: number = 0;
    
    
        // Callbacks
        public onChangeCallbacks: { (data: any): void; } [];
    
    
        constructor(data: GraphData) {
    
            super(data);
            this.onChangeCallbacks = [];
    
    
            this.prepareIds(data);
        }
    
        /**
         * Determines the highest, used ids for GraphElements in data for later use.
         * @param data Data to analyse.
         */
        private prepareIds(data: GraphData) {
            if (data.links.length > 0) {
                this.nextLinkId = this.getHighestId(data.links) + 1;
            }
            if (data.nodes.length > 0) {
                this.nextNodeId = this.getHighestId(data.nodes) + 1;
            }
            if (data.types.length > 0) {
                this.nextTypeId = this.getHighestId(data.types) + 1;
            }
        }
    
        /**
         * Finds the highest id from a list of graph elements.
         * @param elements List of elements containing element with highest id.
         * @returns Highest id in list.
         */
        private getHighestId(elements: GraphElement[]): number {
            let highest: number = 0;
            elements.forEach((element) => {
                if (highest < element.id) {
                    highest = element.id;
                }
            });
            return highest;
    
        }
    
        /**
         * Calls all registered callbacks for the onChange event.
         * @private
         */
        private triggerOnChange() {
            this.onChangeCallbacks.forEach((fn) => fn(this.data));
        }
    
        /**
         * Triggers change event on data-redo.
         */
        protected onRedo() {
            this.triggerOnChange();
        }
    
        /**
         * Triggers change event on data-undo.
         */
        protected onUndo() {
            this.triggerOnChange();
        }
    
    
        protected storableData(data: GraphData): any {
            let clean: GraphData;
            
            clean.links = data.links.map((link) => link.getCleanInstance());
            clean.nodes = data.nodes.map((node) => node.getCleanInstance());
            clean.types = data.types.map((type) => type.getCleanInstance());
    
            return clean;
    
            return this.serializeData(this.data);
        }
    
        /**
         * Takes a data object and serializes it.
         * @param data GraphData object to serialize.
         * @returns Serialized data.
         */
        private serializeData(data: GraphData) : any {
            return {
                ...this.serializeProperties(GRAPH_PARAMS),
                ...this.serializeProperties(GRAPH_DATA_PARAMS, data),
            }
    
        }
    
        /**
         * Adds a pre-created node to the graph.
         * @param node New node object.
         * @returns True, if successful.
         */
        public addNode(node: Node) {
    
            if (this.data.nodes.includes(node)) {
    
                return true;   // Already exists in graph.
            }
    
    
            // Update id
            node.id = this.nextNodeId;
            this.nextNodeId += 1;
    
            this.data.nodes.push(node);
    
    
            this.triggerOnChange();
            // TODO: Use toString implementation of node
            this.storeCurrentData("Added node [" + node + "]");
    
            return true;
        }
    
        /**
         * Deletes a node from the graph.
         * @param node Node object to remove.
         * @returns True, if successful.
         */
        public deleteNode(node: Node): boolean {
    
            if (!this.data.nodes.includes(node)) {
    
                return true;   // Doesn't even exist in graph to begin with.
            }
    
    
            this.data.nodes.filter((n: Node) => n !== node);
    
    
            try {
                // No save points should be created when deleting the links
                this.disableStoring();
    
                // Delete all the links that contain this node
                node.links().forEach((l) => {
                    l.delete();
                });
            } finally {
                this.enableStoring();
            }
    
            this.triggerOnChange();
            // TODO: Use toString implementation of node
            this.storeCurrentData("Deleted node [" + node + "] and all connected links");
    
            return true;
        }
    
        /**
         * Adds a pre-created link to the graph.
         * @param link New link object.
         * @returns True, if successful.
         */
        public addLink(link: Link): boolean {
    
            if (this.data.links.includes(link)) {
    
                return true;   // Already exists in graph.
            }
    
    
            // Updateid
            link.id = this.nextLinkId;
            this.nextLinkId += 1;
    
            this.data.links.push(link);
    
    
            this.triggerOnChange();
            // TODO: Use toString implementation of link
            this.storeCurrentData("Added link [" + link + "]");
    
            return true;
        }
    
        /**
         * Deletes a link from the graph.
         * @param link Link object to remove.
         * @returns True, if successful.
         */
        public deleteLink(link: Link): boolean {
    
            if (!this.data.links.includes(link)) {
    
                return true;   // Doesn't even exist in graph to begin with.
            }
    
    
            this.data.links.filter((l: Link) => l !== link);
    
    
            this.triggerOnChange();
            // TODO: Use toString implementation of link
            this.storeCurrentData("Deleted link [" + link + "]");
    
            return true;
        }
    }