ObjectCache.js

/*
 * Copyright 2020 WICKLETS LLC
 *
 * This file is part of Wick Engine.
 *
 * Wick Engine 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.
 *
 * Wick Engine 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 Wick Engine.  If not, see <https://www.gnu.org/licenses/>.
 */

// NOTE:
// This should probably not be global, and instead, each Wick.Project should own an ObjectCache.
// It's too hard to test if there's a shared ObjectCache between many projects.

/**
 * Global utility class for storing and retrieving large file data.
 */
WickObjectCache = class {
    /**
     * Create a WickObjectCache.
     */
    constructor () {
        this._objects = {};
        this._objectsNeedAutosave = {};
    }

    /**
     * Add an object to the cache.
     * @param {Wick.Base} object - the object to add
     */
    addObject (object) {
        this._objects[object.uuid] = object;

        /*object.children.forEach(child => {
            this.addObject(child);
        });*/
    }

    /**
     * Remove an object from the cache.
     * @param {Wick.Base} object - the object to remove from the cache
     */
    removeObject (object) {
        if (object.classname === 'Project') {
            object.destroy();
            return; // TODO, remove this.
        }
        delete this._objects[object.uuid];
    }

    /**
     * Remove an object from the cache.
     * @param {string} uuid - uuid of the object to remove from the cache
     */
    removeObjectByUUID(uuid) {
        delete this._objects[uuid];
    }

    /**
     * Remove all objects from the Object Cache.
     */
    clear () {
        this._objects = {};
        this._objectsNeedAutosave = {};
    }

    /**
     * Get an object by its UUID.
     * @returns {Wick.Base}
     */
    getObjectByUUID (uuid) {
        if(!uuid) {
            console.error('ObjectCache: getObjectByUUID: uuid is required.');
        }

        var object = this._objects[uuid];
        if(!object) {
            console.error("Warning: object with uuid " + uuid + " was not found in the cache.");
            return null;
        } else {
            return object;
        }
    }

    /**
     * All objects in the cache.
     * @returns {Wick.Base[]}
     */
    getAllObjects () {
        var allObjects = [];

        for (var uuid in this._objects) {
            allObjects.push(this._objects[uuid]);
        }

        return allObjects;
    }

    /**
     * Remove all objects that are in the project, but are no longer linked to the root object.
     * This is basically a garbage collection function. This function attempts to keep objects
     * that are referenced in undo/redo.
     * @param {Wick.Project} project - the project to use to determine which objects have no references
     */
    removeUnusedObjects (project) {
        var activeObjects = this.getActiveObjects(project);
        let uuids = activeObjects.map(obj => obj.uuid);
        uuids.push(project.uuid); // Don't forget to include the project itself...

        let uuidSet = new Set(uuids);

        let historyIDs = project.history.getObjectUUIDs();

        uuidSet = new Set([...historyIDs, ...uuidSet]);

        this.getAllObjects().forEach(object => {
            if(!uuidSet.has(object.uuid)) {
                this.removeObject(object);
            }
        });
    }

    /**
     * Removes all objects with the temporary flag set to true.
     */
    removeTemporaryObjects () {
        this.getAllObjects().forEach(obj => {
            if (obj.temporary) {
                this.removeObject(obj);
            }
        })
    }

    /**
     * Get all objects that are referenced in the given project.
     * @param {Wick.Project} project - the project to check if children are active in.
     * @returns {Wick.Base[]} the active objects.
     */
    getActiveObjects (project) {
        // This does the same thing, but it's WAY faster.
        return project.getChildrenRecursive().map(object => {
            return this.getObjectByUUID(object.uuid);
        });
    }

    /**
     * Saves an object to be autosaved upon the next auto save.
     * @param {Wick.Base} object object to be saved.
     */
    markObjectToBeAutosaved (object) {
        this._objectsNeedAutosave[object.uuid] = true;
    }

    /**
     * Removes a given object from the list of objects that must be autosaved.
     * @param {Wick.Base} object - the object to remove from the list of objects to be autosaved.
     */
    clearObjectToBeAutosaved (object) {
        delete this._objectsNeedAutosave[object.uuid];
    }

    /**
     * Returns true if a given object is marked to be autosaved during the next autosave.
     * @param {Wick.Base} object - the object to check for autosave
     */
    objectNeedsAutosave (object) {
        return Wick.ObjectCache._objectsNeedAutosave[object.uuid];
    }

    /**
     * Returns an array of objects that currently need to be autosaved.
     * @returns {Wick.Base[]} The objects that are marked to be autosaved.
     */
    getObjectsNeedAutosaved () {
        return Object.keys(this._objectsNeedAutosave).map(uuid => this.getObjectByUUID(uuid));
    }
}

Wick.ObjectCache = new WickObjectCache();