/* * Copyright 2003-2017 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.smodel; import jetbrains.mps.extapi.model.SModelDescriptorStub; import jetbrains.mps.project.dependency.ModelDependenciesManager; import jetbrains.mps.project.facets.JavaModuleFacet; import jetbrains.mps.project.facets.TestsFacet; import jetbrains.mps.smodel.language.LanguageRegistry; import jetbrains.mps.util.annotation.ToRemove; import jetbrains.mps.vfs.IFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.language.SAbstractConcept; import org.jetbrains.mps.openapi.language.SLanguage; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.model.SModelReference; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.module.SModule; import org.jetbrains.mps.openapi.module.SModuleReference; import org.jetbrains.mps.openapi.module.SRepository; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; public class SModelOperations { /** * Tell output folder of a model based on its kind and {@linkplain jetbrains.mps.project.facets.GenerationTargetFacet code generation facets} * active for model's module. * * PROVISIONAL CODE. Needed for transition from cumbersome {@link jetbrains.mps.generator.fileGenerator.FileGenerationUtil} to facet-backed output * locations. Doesn't support facets other than {@link JavaModuleFacet} and {@link TestsFacet} * * @return {@code null} if model is not capable to produce output for a model (e.g. deployed/packaged module) * @see jetbrains.mps.project.facets.JavaModuleFacet * @see jetbrains.mps.project.facets.TestsFacet */ @Nullable public static IFile getOutputLocation(@NotNull SModel model) { assert model.getModule() != null; if (SModelStereotype.isTestModel(model)) { TestsFacet facet = model.getModule().getFacet(TestsFacet.class); if (facet != null) { return facet.getOutputLocation(model); } // fall-through } JavaModuleFacet jmf = model.getModule().getFacet(JavaModuleFacet.class); return jmf == null ? null : jmf.getOutputLocation(model); } /** * Pair method to {@link #getOutputLocation(SModel)}, responsible for * {@linkplain jetbrains.mps.project.facets.GenerationTargetFacet#getOutputCacheLocation(SModel) model cache file location}. * * PROVISIONAL CODE. Same considerations as for {@link #getOutputLocation(SModel)} apply. */ @Nullable public static IFile getOutputCacheLocation(@NotNull SModel model) { assert model.getModule() != null; if (SModelStereotype.isTestModel(model)) { TestsFacet facet = model.getModule().getFacet(TestsFacet.class); if (facet != null) { return facet.getOutputCacheLocation(model); } // fall-through } JavaModuleFacet jmf = model.getModule().getFacet(JavaModuleFacet.class); return jmf == null ? null : jmf.getOutputCacheLocation(model); } @Nullable public static SNode getRootByName(SModel model, @NotNull String name) { for (SNode root : model.getRootNodes()) { if (name.equals(root.getName())) return root; } return null; } /** * Plain code (i.e. BaseLanguage and SModel) counterpart for model.nodes(Concept) (i.e. from smodel language) which is translated into * {@link jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations#nodes(SModel, SAbstractConcept)} * <p/> * Primary purpose of this method is to prevent using of FastNodeFinderManager from BL code. * @param model where to look for concept instances, tolerate <code>null</code> * @param concept concept (with sub-concepts) to look up * @return empty collection if model is <code>null</code> or no concept instances found. */ public static List<SNode> getNodes(SModel model, @NotNull SAbstractConcept concept) { if (model == null) { return Collections.emptyList(); } return FastNodeFinderManager.get(model).getNodes(concept, true); } public static boolean isReadOnly(SModel model) { return model.isReadOnly(); } /** * @deprecated Use {@link ModelDependencyUpdate} instead. Although it's a bit different (imports languages first, than * updates model imports; and uses module's scope instead of GMDM to figure out visible models), I don't think any * client relies on this particular behaviour. * Populate given model with proper imports/languages according to actual model content (i.e. nodes) * @param model model to populate with language/import dependencies * @param updateModuleImports <code>true</code> to update imports of model's module (if any) * @param firstVersion whether to use unspecified or actual model version, doesn't make sense for present MPS state (we don't keep these versions in v9), value is ignored */ @Deprecated @ToRemove(version = 2017.1) public static void validateLanguagesAndImports(@NotNull SModel model, boolean updateModuleImports, boolean firstVersion) { ModelDependencyUpdate mdu = new ModelDependencyUpdate(model); mdu.updateUsedLanguages(); mdu.updateImportedModels(null); // throw-away method, don't care to get proper imports if (updateModuleImports && model.getModule() != null && model.getRepository() != null) { mdu.updateModuleDependencies(model.getRepository()); } } /** * All languages visible for the model, including imported and languages they extend * @deprecated 'visible' is vague, whether it's module dependencies or used languages; use SLanguage instead of Language; replace with <code>new SLanguageHierarchy(SModelOperations.getAllLanguageImports()).getExtended()</code> * MPS 3.4 note: despite being deprecated for 1.5 years to date, there are uses of the method. Those in actions are likely to fade away with * new generated code (RT aspects), others deserve attention of the change architect (i.e. accessory models vs language runtime). */ @NotNull @Deprecated @ToRemove(version = 3.2) public static List<Language> getLanguages(SModel model) { // in use in mbeddr ArrayList<Language> languages = new ArrayList<>(); SRepository repository = model.getRepository(); if (repository == null) { // FIXME generator does #validateLanguagesAndImports for detached models, that's why I have to resort to global instance for now. repository = MPSModuleRepository.getInstance(); // throw new IllegalArgumentException("Can't figure out modules for languages of a detached model. Context repository missing"); } LanguageRegistry languageRegistry = LanguageRegistry.getInstance(repository); for (SLanguage lang : new SLanguageHierarchy(languageRegistry, SModelOperations.getAllLanguageImports(model)).getExtended()) { final SModuleReference sourceModuleRef = lang.getSourceModuleReference(); if (sourceModuleRef == null) { continue; } final SModule sourceModule = sourceModuleRef.resolve(repository); if (sourceModule instanceof Language) { languages.add((Language) sourceModule); } } return languages; } /** * Tell used languages of a model the way user specified them in model dependencies. * Doesn't look at actual model content (i.e. what concept instances are there). * <p> * To obtain closure including extended/extending languages, use {@link jetbrains.mps.smodel.SLanguageHierarchy} * @return set of languages imported by the model, either directly or through devkit * @since 3.3 */ @NotNull public static Set<SLanguage> getAllLanguageImports(@NotNull SModel model) { if (model instanceof SModelDescriptorStub) { // if it's our implementation, use cached value return new HashSet<>(((SModelDescriptorStub) model).getModelDepsManager().getAllImportedLanguagesIds()); } // otherwise, just calculate it return new HashSet<>(new ModelDependenciesManager(model).getAllImportedLanguagesIds()); } //todo rewrite using iterators public static List<SModel> allImportedModels(SModel model) { // no uses in mbeddr Set<SModel> result = new LinkedHashSet<SModel>(); result.addAll(importedModels(model)); for (Language language : getLanguages(model)) { List<SModel> accessoryModels = language.getAccessoryModels(); result.addAll(accessoryModels); } result.remove(model); return new ArrayList<SModel>(result); } //todo rewrite using iterators @NotNull public static List<SModelReference> getImportedModelUIDs(SModel sModel) { return new ArrayList<>(new ModelImports(sModel).getImportedModels()); } @NotNull private static List<SModel> importedModels(final SModel model) { List<SModel> modelsList = new ArrayList<SModel>(); for (SModelReference modelReference : new ModelImports(model).getImportedModels()) { SModel modelDescriptor = modelReference.resolve(MPSModuleRepository.getInstance()); if (modelDescriptor != null) { modelsList.add(modelDescriptor); } } return modelsList; } //----------------------------------------------------- }