/* * 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.language; import jetbrains.mps.components.CoreComponent; import jetbrains.mps.core.aspects.behaviour.BehaviorRegistryImpl; import jetbrains.mps.core.aspects.behaviour.api.BehaviorRegistry; import jetbrains.mps.smodel.adapter.ids.MetaIdFactory; import jetbrains.mps.smodel.adapter.ids.MetaIdHelper; import jetbrains.mps.smodel.adapter.ids.SConceptId; import jetbrains.mps.smodel.adapter.structure.concept.InvalidConcept; import jetbrains.mps.smodel.runtime.ConceptDescriptor; import jetbrains.mps.smodel.runtime.ConceptPresentation; import jetbrains.mps.smodel.runtime.ConstraintsDescriptor; import jetbrains.mps.smodel.runtime.illegal.IllegalConceptDescriptor; import jetbrains.mps.util.annotation.ToRemove; import org.jetbrains.annotations.NotNull; import org.jetbrains.mps.openapi.language.SAbstractConcept; import org.jetbrains.mps.openapi.language.SLanguage; import java.util.HashMap; import java.util.Map; // TODO avoid singleton by creating a new ComponentPlugin instance with smodel-related components (it is not CoreComponent in fact) public class ConceptRegistry implements CoreComponent, LanguageRegistryListener { private static Map<String, SAbstractConcept> myConceptByNameCache; private final LanguageRegistry myLanguageRegistry; private final StructureRegistry myStructureRegistry; private final ConceptPropertiesRegistry myConcPropsRegistry; private final BehaviorRegistry myBehaviorRegistry; private final ConstraintsRegistry myConstraintsRegistry; // fixme wrong naming public ConceptRegistry(@NotNull LanguageRegistry languageRegistry) { myLanguageRegistry = languageRegistry; myStructureRegistry = new StructureRegistry(languageRegistry); myConcPropsRegistry = new ConceptPropertiesRegistry(languageRegistry); myBehaviorRegistry = new BehaviorRegistryImpl(languageRegistry); myConstraintsRegistry = new ConstraintsRegistry(languageRegistry); } private static ConceptRegistry INSTANCE; public static ConceptRegistry getInstance() { return INSTANCE; } /** * @deprecated use */ public BehaviorRegistry getBehaviorRegistry() { return myBehaviorRegistry; } @Override public void init() { if (INSTANCE != null) { throw new IllegalStateException("double initialization"); } INSTANCE = this; myLanguageRegistry.addRegistryListener(this); } @Override public void dispose() { myLanguageRegistry.removeRegistryListener(this); INSTANCE = null; } /** * @deprecated It's odd to go from SAbstractConcept back to ConceptDescriptor. It's MPS implementation of SConcept that * deals with ConceptDescriptors to populate SAbstractConcept and client code shall not reverse this. * * * NOTE, THERE ARE NO USES left in MPS code, don't introduce a new one! We'll drop the method any time soon. * * */ @NotNull @Deprecated public ConceptDescriptor getConceptDescriptor(@NotNull SAbstractConcept concept) { SConceptId cid = MetaIdHelper.getConcept(concept); if (cid == MetaIdFactory.INVALID_CONCEPT_ID) { return new IllegalConceptDescriptor(cid, concept.getQualifiedName()); } return getConceptDescriptor(cid); } /** * Looks up {@link ConceptDescriptor} for the given id. If none found, * {@link IllegalConceptDescriptor} instance is returned, with {@link IllegalConceptDescriptor#getId()} * equal to the one supplied. * * @param id identity of a concept (generally, shall not use MetaIdFactory.INVALID_CONCEPT_ID) * @return never {@code null} */ @NotNull public ConceptDescriptor getConceptDescriptor(@NotNull SConceptId id) { // XXX shall I check for id == MetaIdFactory.INVALID_CONCEPT_ID? // If yes, handle gracefully or assert != ConceptDescriptor cd = myStructureRegistry.getConceptDescriptor(id); return cd == null ? new IllegalConceptDescriptor(id) : cd; } public ConceptPresentation getConceptProperties(@NotNull SAbstractConcept concept){ return myConcPropsRegistry.getConceptProperties(concept); } @NotNull public ConstraintsDescriptor getConstraintsDescriptor(@NotNull SAbstractConcept concept) { return myConstraintsRegistry.getConstraintsDescriptor(concept); } @Deprecated @ToRemove(version = 3.4) //this method is here for compatibility purposes. //remve as soon as there's no need in optimizing by-name stuff synchronized public SAbstractConcept getConceptByName(String conceptName) { if (myConceptByNameCache==null) { myConceptByNameCache = new HashMap<String, SAbstractConcept>(); for (SLanguage l : LanguageRegistry.getInstance().getAllLanguages()) { for (SAbstractConcept c : l.getConcepts()) { myConceptByNameCache.put(c.getQualifiedName(),c); } } } SAbstractConcept cached = myConceptByNameCache.get(conceptName); if (cached!=null) return cached; return new InvalidConcept(conceptName); } synchronized private void clearConceptsCache() { myConceptByNameCache = null; } @Override public void beforeLanguagesUnloaded(Iterable<LanguageRuntime> languages) { // no-op, it's not the right time to drop caches (unless can do it selectively) // as other unload listeners might (although should not) access this registry } @Override public void afterLanguagesLoaded(Iterable<LanguageRuntime> languages) { // todo: incremental? myStructureRegistry.clear(); myBehaviorRegistry.clear(); myConstraintsRegistry.clear(); clearConceptsCache(); } }