/* * 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.module; import jetbrains.mps.extapi.module.SRepositoryExt; import jetbrains.mps.model.ModelDeleteHelper; import jetbrains.mps.project.AbstractModule; import jetbrains.mps.project.Project; import jetbrains.mps.project.ProjectBase; import jetbrains.mps.project.facets.JavaModuleFacet; import jetbrains.mps.project.facets.TestsFacet; import jetbrains.mps.project.structure.modules.LanguageDescriptor; import jetbrains.mps.smodel.Generator; import jetbrains.mps.smodel.Language; import jetbrains.mps.smodel.ModuleRepositoryFacade; import jetbrains.mps.vfs.IFile; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.module.SModule; import org.jetbrains.mps.openapi.module.SRepository; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public final class ModuleDeleteHelper { private static final Logger LOG = LogManager.getLogger(ModuleDeleteHelper.class); @NotNull private final Project myProject; private final static String NON_PROJECT_MODULES_MSG = "Non-project modules can only be deleted with files deletion enabled. The module %s will not be deleted"; public ModuleDeleteHelper(@NotNull Project project) { myProject = project; } public void deleteModules(List<SModule> modules, boolean safeDelete, boolean deleteFiles) { if (safeDelete) { LOG.error("SAFE DELETE MODULE - NOT IMPLEMENTED", new Throwable()); } else { modules.stream().filter(m -> m instanceof Language).forEach(m -> { List<SModule> generators = new ArrayList<>(((Language) m).getGenerators()); delete(generators, deleteFiles); }); delete(modules, deleteFiles); } } private void delete(@NotNull List<SModule> modules, boolean deleteFiles) { modules = new ArrayList<>(modules); checkNonProjectModules(modules, deleteFiles); // fixme: MPS-18743 modules.stream().filter(module -> module instanceof AbstractModule).forEach(module -> ((AbstractModule) module).save()); if (deleteFiles) { modules.forEach(this::deleteModuleFiles); } modules.forEach(this::removeFromProject); if (deleteFiles) { ModuleRepositoryFacade facade = new ModuleRepositoryFacade(myProject.getRepository()); modules.forEach(facade::unregisterModule); } } public void deleteModuleFiles(@NotNull SModule module) { for (SModel model : module.getModels()) { new ModelDeleteHelper(model).delete(); } deleteJavaFacet(module); deleteTestsFacet(module); if (module instanceof AbstractModule) { AbstractModule curModule = (AbstractModule) module; deleteFile(curModule.getDescriptorFile()); if (curModule.getModuleSourceDir() != null && deleteDirIfEmpty(curModule.getModuleSourceDir())) { deleteFile(curModule.getModuleSourceDir()); } if (curModule.getDescriptorFile() != null) { IFile moduleFolder = curModule.getDescriptorFile().getParent(); if (moduleFolder != null && deleteDirIfEmpty(moduleFolder)) { moduleFolder.delete(); } } } } private void checkNonProjectModules(List<SModule> modules, boolean deleteFiles) { if (!deleteFiles) { for (Iterator<SModule> iterator = modules.iterator(); iterator.hasNext(); ) { SModule module = iterator.next(); SModule module0 = module; if (module instanceof Generator) { module0 = ((Generator) module).getSourceLanguage(); } if (!myProject.isProjectModule(module0)) { LOG.warn(String.format(NON_PROJECT_MODULES_MSG, module), new Exception()); iterator.remove(); } } } } private void removeFromProject(SModule module) { //remove from project if (myProject.isProjectModule(module)) { final SRepository repository = myProject.getRepository(); if (repository instanceof SRepositoryExt) { ((SRepositoryExt) repository).unregisterModule(module, myProject); } myProject.removeModule(module); ((ProjectBase) myProject).save(); } // TODO: remove after Generator will be moved it's own descriptor file // Second parameter prevent exceptions after Generator extraction from Language if (module instanceof Generator && ((Generator) module).getDescriptorFile() == null) { // This logic was taken from DeleteGeneratorHelper#delete() method final Language sourceLanguage = ((Generator) module).getSourceLanguage(); LanguageDescriptor languageDescriptor = sourceLanguage.getModuleDescriptor(); languageDescriptor.getGenerators().remove(((Generator) module).getModuleDescriptor()); sourceLanguage.setModuleDescriptor(languageDescriptor); sourceLanguage.reload(); sourceLanguage.save(); } } private static void deleteTestsFacet(SModule module) { TestsFacet testsFacet = module.getFacet(TestsFacet.class); if (testsFacet == null) { return; } deleteFile(testsFacet.getTestsOutputPath()); deleteFile(testsFacet.getOutputCacheRoot()); } private static void deleteJavaFacet(SModule module) { JavaModuleFacet javaModuleFacet = module.getFacet(JavaModuleFacet.class); if (javaModuleFacet == null) { return; } deleteFile(javaModuleFacet.getClassesGen()); deleteFile(javaModuleFacet.getOutputRoot()); deleteFile(javaModuleFacet.getOutputCacheRoot()); } private static void deleteFile(@Nullable IFile file) { if (file != null && file.exists()) { // FIXME is there true need to check for existence file that gonna be deleted? Does delete() tolerate non-existent files? file.delete(); } } private static boolean deleteDirIfEmpty(@NotNull IFile file) { if (!file.exists()) { return true; } if (!file.isDirectory()) { return false; } List<IFile> children = file.getChildren(); if (file.isDirectory() && children.isEmpty()) { return true; } assert children != null : "IFile.getChildren() == null iff !isDirectory"; for (IFile child : children) { if (!deleteDirIfEmpty(child)) { return false; } } return true; } }