com.google.appinventor.client.editor.youngandroid.YaBlocksEditor.java Source code

Java tutorial

Introduction

Here is the source code for com.google.appinventor.client.editor.youngandroid.YaBlocksEditor.java

Source

// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package com.google.appinventor.client.editor.youngandroid;

import com.google.appinventor.client.Ode;
import com.google.appinventor.client.OdeAsyncCallback;
import com.google.appinventor.client.boxes.AssetListBox;
import com.google.appinventor.client.boxes.BlockSelectorBox;
import com.google.appinventor.client.boxes.PaletteBox;
import com.google.appinventor.client.editor.FileEditor;
import com.google.appinventor.client.editor.simple.SimpleComponentDatabase;
import com.google.appinventor.client.editor.simple.components.FormChangeListener;
import com.google.appinventor.client.editor.simple.components.MockComponent;
import com.google.appinventor.client.editor.simple.components.MockForm;
import com.google.appinventor.client.editor.simple.palette.DropTargetProvider;
import com.google.appinventor.client.editor.youngandroid.palette.YoungAndroidPalettePanel;
import com.google.appinventor.client.explorer.SourceStructureExplorer;
import com.google.appinventor.client.explorer.SourceStructureExplorerItem;
import com.google.appinventor.client.explorer.project.ComponentDatabaseChangeListener;
import com.google.appinventor.client.output.OdeLog;
import com.google.appinventor.client.widgets.dnd.DropTarget;
import com.google.appinventor.components.common.YaVersion;
import com.google.appinventor.shared.rpc.project.ChecksumedFileException;
import com.google.appinventor.shared.rpc.project.ChecksumedLoadFile;
import com.google.appinventor.shared.rpc.project.FileDescriptorWithContent;
import com.google.appinventor.shared.rpc.project.youngandroid.YoungAndroidBlocksNode;
import com.google.appinventor.shared.youngandroid.YoungAndroidSourceAnalyzer;
import com.google.common.collect.Maps;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.TreeItem;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.google.appinventor.client.Ode.MESSAGES;

/**
 * Editor for Young Android Blocks (.blk) files.
 *
 * TODO(sharon): blocks file loading and saving is not implemented yet!!
 *
 * @author lizlooney@google.com (Liz Looney)
 * @author sharon@google.com (Sharon Perl) added Blockly functionality
 */
public final class YaBlocksEditor extends FileEditor
        implements FormChangeListener, BlockDrawerSelectionListener, ComponentDatabaseChangeListener {

    // A constant to substract from the total height of the Viewer window, set through
    // the computed height of the user's window (Window.getClientHeight())
    // This is an approximation of the size of the header navigation panel
    private static final int VIEWER_WINDOW_OFFSET = 170;

    // Database of component type descriptions
    private final SimpleComponentDatabase COMPONENT_DATABASE;

    // Keep a map from projectid_formname -> YaBlocksEditor for handling blocks workspace changed
    // callbacks from the BlocklyPanel objects. This has to be static because it is used by
    // static methods that are called from the Javascript Blockly world.
    private static final Map<String, YaBlocksEditor> formToBlocksEditor = Maps.newHashMap();

    // projectid_formname for this blocks editor. Our index into the static formToBlocksEditor map.
    private String fullFormName;

    private final YoungAndroidBlocksNode blocksNode;

    // References to other panels that we need to control.
    private final SourceStructureExplorer sourceStructureExplorer;

    // Panel that is used as the content of the palette box
    private final YoungAndroidPalettePanel palettePanel;

    // Blocks area. Note that the blocks area is a part of the "document" in the
    // browser (via the deckPanel in the ProjectEditor). So if the document changes (which happens
    // when we switch projects) we will lose the blocks editor state, even though
    // YaBlocksEditor objects are kept around when switching projects. If we come
    // back to this blocks editor after having switched projects, the blocksArea
    // will get reinitialized.
    private final BlocklyPanel blocksArea;

    // True once we've finished loading the current file.
    private boolean loadComplete = false;

    // if selectedDrawer != null, it is either "component_" + instance name or
    // "builtin_" + drawer name
    private String selectedDrawer = null;

    // Keep a list of components that we know about. Need this to detect when a call to add a
    // component is adding one that we already have (which can happen when a component gets
    // moved from one container to another). In that case we do not want to add it to the
    // blocks area again.
    private Set<String> componentUuids = new HashSet<String>();

    // The form editor associated with this blocks editor
    private YaFormEditor myFormEditor;

    // Used to determine if the newly generated yail should be sent to the debugging phone
    private String lastYail = "";

    //Timer used to poll blocks editor to check if it is initialized
    private static Timer timer;

    private final List<ComponentDatabaseChangeListener> componentDatabaseChangeListeners = new ArrayList<ComponentDatabaseChangeListener>();

    YaBlocksEditor(YaProjectEditor projectEditor, YoungAndroidBlocksNode blocksNode) {
        super(projectEditor, blocksNode);

        this.blocksNode = blocksNode;
        COMPONENT_DATABASE = SimpleComponentDatabase.getInstance(getProjectId());

        fullFormName = blocksNode.getProjectId() + "_" + blocksNode.getFormName();
        formToBlocksEditor.put(fullFormName, this);
        blocksArea = new BlocklyPanel(this, fullFormName); // [lyn, 2014/10/28] pass in editor so can extract form json from it
        blocksArea.setWidth("100%");
        // This code seems to be using a rather old layout, so we cannot simply pass 100% for height.
        // Instead, it needs to be calculated from the client's window, and a listener added to Window
        // We use VIEWER_WINDOW_OFFSET as an approximation of the size of the top navigation bar
        // New layouts don't need all this messing; see comments on selected answer at:
        // http://stackoverflow.com/questions/86901/creating-a-fluid-panel-in-gwt-to-fill-the-page
        blocksArea.setHeight(Window.getClientHeight() - VIEWER_WINDOW_OFFSET + "px");
        Window.addResizeHandler(new ResizeHandler() {
            public void onResize(ResizeEvent event) {
                int height = event.getHeight();
                blocksArea.setHeight(height - VIEWER_WINDOW_OFFSET + "px");
            }
        });
        initWidget(blocksArea);
        addComponentDatabaseChangeListener(blocksArea);

        // Get references to the source structure explorer
        sourceStructureExplorer = BlockSelectorBox.getBlockSelectorBox().getSourceStructureExplorer();

        // Listen for selection events for built-in drawers
        BlockSelectorBox.getBlockSelectorBox().addBlockDrawerSelectionListener(this);

        // Create palettePanel, which will be used as the content of the PaletteBox.
        myFormEditor = projectEditor.getFormFileEditor(blocksNode.getFormName());
        if (myFormEditor != null) {
            palettePanel = new YoungAndroidPalettePanel(myFormEditor);
            palettePanel.loadComponents(new DropTargetProvider() {
                // TODO(sharon): make the tree in the BlockSelectorBox a drop target
                @Override
                public DropTarget[] getDropTargets() {
                    return new DropTarget[0];
                }
            });
            palettePanel.setSize("100%", "100%");
        } else {
            palettePanel = null;
            OdeLog.wlog("Can't get form editor for blocks: " + getFileId());
        }
    }

    // FileEditor methods

    @Override
    public void loadFile(final Command afterFileLoaded) {
        final long projectId = getProjectId();
        final String fileId = getFileId();
        OdeAsyncCallback<ChecksumedLoadFile> callback = new OdeAsyncCallback<ChecksumedLoadFile>(
                MESSAGES.loadError()) {
            @Override
            public void onSuccess(ChecksumedLoadFile result) {
                String blkFileContent;
                try {
                    blkFileContent = result.getContent();
                } catch (ChecksumedFileException e) {
                    this.onFailure(e);
                    return;
                }
                String formJson = myFormEditor.preUpgradeJsonString(); // [lyn, 2014/10/27] added formJson for upgrading
                blocksArea.loadBlocksContent(formJson, blkFileContent);
                loadComplete = true;
                selectedDrawer = null;
                if (afterFileLoaded != null) {
                    afterFileLoaded.execute();
                }
            }

            @Override
            public void onFailure(Throwable caught) {
                if (caught instanceof ChecksumedFileException) {
                    Ode.getInstance().recordCorruptProject(projectId, fileId, caught.getMessage());
                }
                super.onFailure(caught);
            }
        };
        Ode.getInstance().getProjectService().load2(projectId, fileId, callback);
    }

    @Override
    public String getTabText() {
        return MESSAGES.blocksEditorTabName(blocksNode.getFormName());
    }

    @Override
    public void onShow() {
        OdeLog.log("YaBlocksEditor: got onShow() for " + getFileId());
        super.onShow();
        showWhenInitialized();
    }

    public void showWhenInitialized() {
        //check if blocks are initialized
        if (BlocklyPanel.blocksInited(fullFormName)) {
            blocksArea.showDifferentForm(fullFormName);
            loadBlocksEditor();
            sendComponentData(); // Send Blockly the component information for generating Yail
            blocksArea.renderBlockly(); //Re-render Blockly due to firefox bug
        } else {
            //timer calls this function again if the blocks are not initialized
            if (timer == null) {
                timer = new Timer() {
                    public void run() {
                        showWhenInitialized();
                    }
                };
            }
            timer.schedule(200); // Run every 200 milliseconds
        }
    }

    /*
     * Updates the the whole designer: form, palette, source structure explorer, assets list, and
     * properties panel.
     */
    private void loadBlocksEditor() {

        // Set the palette box's content.
        if (palettePanel != null) {
            PaletteBox paletteBox = PaletteBox.getPaletteBox();
            paletteBox.setContent(palettePanel);
        }
        PaletteBox.getPaletteBox().setVisible(false);

        // Update the source structure explorer with the tree of this form's components.
        MockForm form = getForm();
        if (form != null) {
            // start with no component selected in sourceStructureExplorer. We
            // don't want a component drawer open in the blocks editor when we
            // come back to it.
            updateBlocksTree(form, null);

            Ode.getInstance().getWorkColumns().remove(Ode.getInstance().getStructureAndAssets().getWidget(2));
            Ode.getInstance().getWorkColumns().insert(Ode.getInstance().getStructureAndAssets(), 1);
            Ode.getInstance().getStructureAndAssets().insert(BlockSelectorBox.getBlockSelectorBox(), 0);
            BlockSelectorBox.getBlockSelectorBox().setVisible(true);
            AssetListBox.getAssetListBox().setVisible(true);
            hideComponentBlocks();
        } else {
            OdeLog.wlog("Can't get form editor for blocks: " + getFileId());
        }
    }

    @Override
    public void onHide() {
        // When an editor is detached, if we are the "current" editor,
        // set the current editor to null and clean up the UI.
        // Note: I'm not sure it is possible that we would not be the "current"
        // editor when this is called, but we check just to be safe.
        OdeLog.log("YaBlocksEditor: got onHide() for " + getFileId());
        if (Ode.getInstance().getCurrentFileEditor() == this) {
            super.onHide();
            unloadBlocksEditor();
        } else {
            OdeLog.wlog("YaBlocksEditor.onHide: Not doing anything since we're not the " + "current file editor!");
        }
    }

    @Override
    public void onClose() {
        // our partner YaFormEditor added us as a FormChangeListener, but we remove ourself.
        getForm().removeFormChangeListener(this);
        BlockSelectorBox.getBlockSelectorBox().removeBlockDrawerSelectionListener(this);
        formToBlocksEditor.remove(fullFormName);
    }

    public static void toggleWarning() {
        BlocklyPanel.switchWarningVisibility();
        for (Object formName : formToBlocksEditor.keySet().toArray()) {
            BlocklyPanel.toggleWarning((String) formName);
        }
    }

    private void unloadBlocksEditor() {
        // TODO(sharon): do something about form change listener?

        // Clear the palette box.
        PaletteBox paletteBox = PaletteBox.getPaletteBox();
        paletteBox.clear();
        paletteBox.setVisible(true);

        Ode.getInstance().getWorkColumns().remove(Ode.getInstance().getStructureAndAssets().getWidget(0));
        Ode.getInstance().getWorkColumns().insert(Ode.getInstance().getStructureAndAssets(), 3);
        Ode.getInstance().getStructureAndAssets().insert(BlockSelectorBox.getBlockSelectorBox(), 0);
        BlockSelectorBox.getBlockSelectorBox().setVisible(false);
        AssetListBox.getAssetListBox().setVisible(true);

        // Clear and hide the blocks selector tree
        sourceStructureExplorer.clearTree();
        hideComponentBlocks();
    }

    public static void onBlocksAreaChanged(String formName) {
        YaBlocksEditor editor = formToBlocksEditor.get(formName);
        if (editor != null) {
            OdeLog.log("Got blocks area changed for " + formName);
            Ode.getInstance().getEditorManager().scheduleAutoSave(editor);
            if (editor instanceof YaBlocksEditor)
                editor.sendComponentData();
        }
    }

    public synchronized void sendComponentData() {
        try {
            blocksArea.sendComponentData(myFormEditor.encodeFormAsJsonString(true),
                    packageNameFromPath(getFileId()));
        } catch (YailGenerationException e) {
            e.printStackTrace();
        }
    }

    private void updateBlocksTree(MockForm form, SourceStructureExplorerItem itemToSelect) {
        TreeItem items[] = new TreeItem[3];
        items[0] = BlockSelectorBox.getBlockSelectorBox().getBuiltInBlocksTree();
        items[1] = form.buildComponentsTree();
        items[2] = BlockSelectorBox.getBlockSelectorBox().getGenericComponentsTree(form);
        sourceStructureExplorer.updateTree(items, itemToSelect);
    }

    // Do whatever is needed to save Blockly state when our project is about to be
    // detached from the parent document. Note that this is not for saving the blocks file itself.
    // We use EditorManager.scheduleAutoSave for that.
    public void prepareForUnload() {
        blocksArea.saveComponentsAndBlocks();
        //    blocksArea.saveBackpackContents();
    }

    @Override
    public String getRawFileContent() {
        return blocksArea.getBlocksContent();
    }

    public FileDescriptorWithContent getYail() throws YailGenerationException {
        return new FileDescriptorWithContent(getProjectId(), yailFileName(),
                blocksArea.getYail(myFormEditor.encodeFormAsJsonString(true), packageNameFromPath(getFileId())));
    }

    /**
     * Converts a source file path (e.g.,
     * src/com/gmail/username/project1/Form.extension) into a package
     * name (e.g., com.gmail.username.project1.Form)
     * @param path the path to convert.
     * @return a dot separated package name.
     */
    private static String packageNameFromPath(String path) {
        path = path.replaceFirst("src/", "");
        int extensionIndex = path.lastIndexOf(".");
        if (extensionIndex != -1) {
            path = path.substring(0, extensionIndex);
        }
        return path.replaceAll("/", ".");
    }

    @Override
    public void onSave() {
        // Nothing to do after blocks are saved.
    }

    public static String getComponentInfo(String typeName) {
        return SimpleComponentDatabase.getInstance().getTypeDescription(typeName);
    }

    public static String getComponentsJSONString() {
        return SimpleComponentDatabase.getInstance().getComponentsJSONString();
    }

    public static String getComponentInstanceTypeName(String formName, String instanceName) {
        //use form name to get blocks editor
        YaBlocksEditor blocksEditor = formToBlocksEditor.get(formName);
        //get type name from form editor
        return blocksEditor.myFormEditor.getComponentInstanceTypeName(instanceName);
    }

    public void addComponent(String typeName, String instanceName, String uuid) {
        if (componentUuids.add(uuid)) {
            String typeDescription = COMPONENT_DATABASE.getTypeDescription(typeName);
            blocksArea.addComponent(typeDescription, instanceName, uuid);
        }
    }

    public void removeComponent(String typeName, String instanceName, String uuid) {
        if (componentUuids.remove(uuid)) {
            blocksArea.removeComponent(typeName, instanceName, uuid);
        }
    }

    public void renameComponent(String typeName, String oldName, String newName, String uuid) {
        blocksArea.renameComponent(typeName, oldName, newName, uuid);
    }

    public void showComponentBlocks(String instanceName) {
        String instanceDrawer = "component_" + instanceName;
        if (selectedDrawer == null || !blocksArea.drawerShowing() || !selectedDrawer.equals(instanceDrawer)) {
            blocksArea.showComponentBlocks(instanceName);
            selectedDrawer = instanceDrawer;
        } else {
            blocksArea.hideComponentBlocks();
            selectedDrawer = null;
        }
    }

    public void hideComponentBlocks() {
        blocksArea.hideComponentBlocks();
        selectedDrawer = null;
    }

    public void showBuiltinBlocks(String drawerName) {
        OdeLog.log("Showing built-in drawer " + drawerName);
        String builtinDrawer = "builtin_" + drawerName;
        if (selectedDrawer == null || !blocksArea.drawerShowing() || !selectedDrawer.equals(builtinDrawer)) {
            blocksArea.showBuiltinBlocks(drawerName);
            selectedDrawer = builtinDrawer;
        } else {
            blocksArea.hideBuiltinBlocks();
            selectedDrawer = null;
        }
    }

    public void showGenericBlocks(String drawerName) {
        OdeLog.log("Showing generic drawer " + drawerName);
        String genericDrawer = "generic_" + drawerName;
        if (selectedDrawer == null || !blocksArea.drawerShowing() || !selectedDrawer.equals(genericDrawer)) {
            blocksArea.showGenericBlocks(drawerName);
            selectedDrawer = genericDrawer;
        } else {
            blocksArea.hideGenericBlocks();
            selectedDrawer = null;
        }
    }

    public void hideBuiltinBlocks() {
        blocksArea.hideBuiltinBlocks();
    }

    public MockForm getForm() {
        YaProjectEditor yaProjectEditor = (YaProjectEditor) projectEditor;
        YaFormEditor myFormEditor = yaProjectEditor.getFormFileEditor(blocksNode.getFormName());
        if (myFormEditor != null) {
            return myFormEditor.getForm();
        } else {
            return null;
        }
    }

    private String yailFileName() {
        String fileId = getFileId();
        return fileId.replace(YoungAndroidSourceAnalyzer.BLOCKLY_SOURCE_EXTENSION,
                YoungAndroidSourceAnalyzer.YAIL_FILE_EXTENSION);
    }

    // FormChangeListener implementation
    // Note: our companion YaFormEditor adds us as a listener on the form

    /*
     * @see com.google.appinventor.client.editor.simple.components.FormChangeListener#
     * onComponentPropertyChanged
     * (com.google.appinventor.client.editor.simple.components.MockComponent, java.lang.String,
     * java.lang.String)
     */
    @Override
    public void onComponentPropertyChanged(MockComponent component, String propertyName, String propertyValue) {
        // nothing to do here
    }

    /*
     * @see
     * com.google.appinventor.client.editor.simple.components.FormChangeListener#onComponentRemoved
     * (com.google.appinventor.client.editor.simple.components.MockComponent, boolean)
     */
    @Override
    public void onComponentRemoved(MockComponent component, boolean permanentlyDeleted) {
        if (permanentlyDeleted) {
            removeComponent(component.getType(), component.getName(), component.getUuid());
            if (loadComplete) {
                updateSourceStructureExplorer();
            }
        }
    }

    /*
     * @see
     * com.google.appinventor.client.editor.simple.components.FormChangeListener#onComponentAdded
     * (com.google.appinventor.client.editor.simple.components.MockComponent)
     */
    @Override
    public void onComponentAdded(MockComponent component) {
        addComponent(component.getType(), component.getName(), component.getUuid());
        if (loadComplete) {
            // Update source structure panel
            updateSourceStructureExplorer();
        }
    }

    /*
     * @see
     * com.google.appinventor.client.editor.simple.components.FormChangeListener#onComponentRenamed
     * (com.google.appinventor.client.editor.simple.components.MockComponent, java.lang.String)
     */
    @Override
    public void onComponentRenamed(MockComponent component, String oldName) {
        renameComponent(component.getType(), oldName, component.getName(), component.getUuid());
        if (loadComplete) {
            updateSourceStructureExplorer();
            // renaming could potentially confuse an open drawer so close just in case
            hideComponentBlocks();
            selectedDrawer = null;
        }
    }

    private void updateSourceStructureExplorer() {
        MockForm form = getForm();
        if (form != null) {
            updateBlocksTree(form, form.getSelectedComponent().getSourceStructureExplorerItem());
        }
    }

    /*
     * @see com.google.appinventor.client.editor.simple.components.FormChangeListener#
     * onComponentSelectionChange
     * (com.google.appinventor.client.editor.simple.components.MockComponent, boolean)
     */
    @Override
    public void onComponentSelectionChange(MockComponent component, boolean selected) {
        // not relevant for blocks editor - this happens on clicks in the mock form areas
    }

    // BlockDrawerSelectionListener implementation

    /*
     * @see com.google.appinventor.client.editor.youngandroid.BlockDrawerSelectionListener#
     * onBlockDrawerSelected(java.lang.String)
     */
    @Override
    public void onBuiltinDrawerSelected(String drawerName) {
        // Only do something if we are the current file editor
        if (Ode.getInstance().getCurrentFileEditor() == this) {
            showBuiltinBlocks(drawerName);
        }
    }

    /*
     * @see com.google.appinventor.client.editor.youngandroid.BlockDrawerSelectionListener#
     * onBlockDrawerSelected(java.lang.String)
     */
    @Override
    public void onGenericDrawerSelected(String drawerName) {
        // Only do something if we are the current file editor
        if (Ode.getInstance().getCurrentFileEditor() == this) {
            showGenericBlocks(drawerName);
        }
    }

    /*
     * Start up the Repl (call into the Blockly.ReplMgr via the BlocklyPanel.
     */
    @Override
    public void startRepl(boolean alreadyRunning, boolean forEmulator, boolean forUsb) {
        blocksArea.startRepl(alreadyRunning, forEmulator, forUsb);
    }

    /*
     * Perform a Hard Reset of the Emulator
     */
    public void hardReset() {
        blocksArea.hardReset();
    }

    // Static Function. Find the associated editor for formName and
    // set its "damaged" bit. This will cause the editor manager's scheduleAutoSave
    // method to ignore this blocks file and not save it out.

    public static void setBlocksDamaged(String formName) {
        YaBlocksEditor editor = formToBlocksEditor.get(formName);
        if (editor != null) {
            editor.setDamaged(true);
        }
    }

    /*
     * Switch language to the specified language if applicable
     */
    @Override
    public void switchLanguage(String newLanguage) {
        blocksArea.switchLanguage(newLanguage);
    }

    /*
     * Trigger a Companion Update
     */
    @Override
    public void updateCompanion() {
        blocksArea.updateCompanion();
    }

    /*
     * [lyn, 2014/10/28] Added for accessing current form json from BlocklyPanel
     * Encodes the associated form's properties as a JSON encoded string. Used by YaBlocksEditor as well,
     * to send the form info to the blockly world during code generation.
     */
    protected String encodeFormAsJsonString(boolean forYail) {
        return myFormEditor.encodeFormAsJsonString(forYail);
    }

    private void addComponentDatabaseChangeListener(ComponentDatabaseChangeListener cdbChangeListener) {
        componentDatabaseChangeListeners.add(cdbChangeListener);
    }

    private void removeComponentDatabaseChangeListener(ComponentDatabaseChangeListener cdbChangeListener) {
        componentDatabaseChangeListeners.remove(cdbChangeListener);
    }

    private void clearComponentDatabaseChangeListener() {
        componentDatabaseChangeListeners.clear();
    }

    @Override
    public void onComponentTypeAdded(List<String> componentTypes) {
        COMPONENT_DATABASE.removeComponentDatabaseListener(this);
        for (ComponentDatabaseChangeListener cdbChangeListener : componentDatabaseChangeListeners) {
            cdbChangeListener.onComponentTypeAdded(componentTypes);
        }
    }

    @Override
    public boolean beforeComponentTypeRemoved(List<String> componentTypes) {
        boolean result = true;
        for (ComponentDatabaseChangeListener cdbChangeListener : componentDatabaseChangeListeners) {
            result = result & cdbChangeListener.beforeComponentTypeRemoved(componentTypes);
        }
        return result;
    }

    @Override
    public void onComponentTypeRemoved(Map<String, String> componentTypes) {
        COMPONENT_DATABASE.removeComponentDatabaseListener(this);
        for (ComponentDatabaseChangeListener cdbChangeListener : componentDatabaseChangeListeners) {
            cdbChangeListener.onComponentTypeRemoved(componentTypes);
        }
    }

    @Override
    public void onResetDatabase() {
        COMPONENT_DATABASE.removeComponentDatabaseListener(this);
        for (ComponentDatabaseChangeListener cdbChangeListener : componentDatabaseChangeListeners) {
            cdbChangeListener.onResetDatabase();
        }
    }

}