/* * Copyright 2003-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.model; import jetbrains.mps.extapi.model.SModelBase; import jetbrains.mps.extapi.module.SModuleBase; import jetbrains.mps.extapi.persistence.DisposableDataSource; import jetbrains.mps.project.facets.GenerationTargetFacet; import jetbrains.mps.project.facets.JavaModuleFacet; import jetbrains.mps.vfs.IFile; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.module.SModule; import org.jetbrains.mps.openapi.module.SModuleFacet; import org.jetbrains.mps.openapi.persistence.DataSource; import java.util.ArrayDeque; /** * Utility to perform various aspects of expelling a model: * - Model may produce generated files that need to be removed * - Models come from a data source that needs to be removed as well * - Models belong to a module that needs to get notified about model gone * * All these shall be part of regular API. There's no such API yet, unfortunately. * To keep relevant stuff in a single place, accessible not only to UI code (like j.m.workbench.action.model.DeleteModelHelper does), * this class was introduced. Its aspects shall move to respective classes (e.g. DataSource shall get #delete() API, SModule needs #detach(SModel), etc) * Use either {@link #delete()} for complete disposal of a model, or distinct step * ({@link #removeGeneratedArtifacts()}, {@link #detachFromModule()} or {@link #deleteDataSource()}) as appropriate. * * Lives in [kernel] as it depends from implementation classes now (like SModuleBase). * * @author Artem Tikhomirov * @since 3.4 */ public class ModelDeleteHelper { private final SModel myModel; public ModelDeleteHelper(@NotNull SModel model) { myModel = model; } public void delete() { removeGeneratedArtifacts(); detachFromModule(); deleteDataSource(); } public void removeGeneratedArtifacts() { SModule module = myModel.getModule(); JavaModuleFacet javaFacet = module.getFacet(JavaModuleFacet.class); // perhaps, worth an option (pruneEmpty)? ArrayDeque<IFile> possiblyEmptyDirsToPrune = new ArrayDeque<>(); if (javaFacet != null) { IFile classesGenDir = javaFacet.getClassesLocation(myModel); if (classesGenDir != null) { possiblyEmptyDirsToPrune.add(classesGenDir.getParent()); // I don't expect model output dir to be top of the disk, don't care about parent == null classesGenDir.delete(); } } for (SModuleFacet mf : module.getFacets()) { if (!(mf instanceof GenerationTargetFacet)) { continue; } GenerationTargetFacet genFacet = (GenerationTargetFacet) mf; IFile modelOutput = genFacet.getOutputLocation(myModel); IFile modelCaches = genFacet.getOutputCacheLocation(myModel); // XXX if model serves as a namespace, there might be child folders with output for nested models // nevertheless, we delete them altogether as removal of a model likely means removal of nested models as well. Or not? // e.g. IdeCommandUtil is more careful when executing #removeGenSources console command if (modelOutput != null) { possiblyEmptyDirsToPrune.add(modelOutput.getParent()); modelOutput.delete(); } if (modelCaches != null) { possiblyEmptyDirsToPrune.add(modelCaches.getParent()); modelCaches.delete(); } } // FIXME what about TestsFacet, why not deleting its artifacts? while (!possiblyEmptyDirsToPrune.isEmpty()) { IFile d = possiblyEmptyDirsToPrune.removeFirst(); if (d.exists() && d.getChildren().isEmpty()) { IFile parent = d.getParent(); if (parent != null) { possiblyEmptyDirsToPrune.addLast(parent); } d.delete(); } } } public void detachFromModule() { SModule module = myModel.getModule(); if (module == null) { return; } ((SModuleBase) module).unregisterModel((SModelBase) myModel); } public void deleteDataSource() { DataSource source = myModel.getSource(); String modelName = myModel.getModelName(); if (source instanceof DisposableDataSource) { ((DisposableDataSource) source).delete(); } else { String msg = String.format("DataSource (%s) of model %s doesn't support delete operation.", source.getClass().getName(), modelName); Logger.getLogger(ModelDeleteHelper.class).info(msg); } } }