/* * Copyright 2003-2015 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.persistence.def.v9; import jetbrains.mps.persistence.MetaModelInfoProvider; import jetbrains.mps.persistence.registry.ConceptInfo; import jetbrains.mps.persistence.registry.IdInfoRegistry; import jetbrains.mps.persistence.registry.LangInfo; import jetbrains.mps.smodel.adapter.ids.SConceptId; import jetbrains.mps.smodel.adapter.ids.SContainmentLinkId; import jetbrains.mps.smodel.adapter.ids.SLanguageId; import jetbrains.mps.smodel.adapter.ids.SPropertyId; import jetbrains.mps.smodel.adapter.ids.SReferenceLinkId; import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory; import jetbrains.mps.smodel.runtime.ConceptKind; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.language.SConcept; import org.jetbrains.mps.openapi.language.SContainmentLink; import org.jetbrains.mps.openapi.language.SLanguage; import org.jetbrains.mps.openapi.language.SProperty; import org.jetbrains.mps.openapi.language.SReferenceLink; import java.util.HashMap; import java.util.Map; /** * Facility to read meta-model information persisted in a model file, to fill {@link jetbrains.mps.smodel.persistence.def.v9.IdInfoCollector} back from the * serialized registry. Serves the task to parametrize ModelReader as well. * * Although barely a mediator to few other facilities, grabs great portion of code one would otherwise write in ModelReaderHandler. * * Stateful, withLanguage() identifies language for subsequent withConcept, which, furthermore, identify concept for any * subsequent #property(), #association() and #aggregation call. */ class IdInfoReadHelper { private final IdInfoRegistry myMetaRegistry; private final IdEncoder myIdEncoder; private final MetaModelInfoProvider myMetaInfoProvider; private LangInfo myActualLang; private ConceptInfo myActualConcept; private final Map<String, SConcept> myConcepts = new HashMap<String, SConcept>(); private final Map<String, SProperty> myProperties = new HashMap<String, SProperty>(); private final Map<String, SReferenceLink> myAssociations = new HashMap<String, SReferenceLink>(); private final Map<String, SContainmentLink> myAggregations = new HashMap<String, SContainmentLink>(); private final boolean myInterfaceOnly; private final boolean myStripImplementation; public IdInfoReadHelper(@NotNull MetaModelInfoProvider mmiProvider, boolean interfaceOnly, boolean stripImplementation) { myMetaInfoProvider = mmiProvider; myIdEncoder = new IdEncoder(); myMetaRegistry = new IdInfoRegistry(); myInterfaceOnly = interfaceOnly; myStripImplementation = stripImplementation; } @NotNull public IdEncoder getIdEncoder() { return myIdEncoder; } public boolean isRequestedInterfaceOnly() { return myInterfaceOnly; } public boolean isRequestedStripImplementation() { return myStripImplementation; } // Fill methods, populate myInfoCollector with persisted meta-model info public void withLanguage(String id, String name) { final SLanguageId languageId = myIdEncoder.parseLanguageId(id); myActualLang = myMetaRegistry.registerLanguage(languageId, name); myMetaInfoProvider.setLanguageName(languageId, name); } // @param stub is optional public void withConcept(String id, String name, String index, String nodeInfo, String stub) { assert myActualLang != null; SConceptId conceptId = myIdEncoder.parseConceptId(myActualLang.getLanguageId(), id); myActualConcept = myMetaRegistry.registerConcept(conceptId, name); myActualConcept.parseImplementationKind(nodeInfo); myConcepts.put(index, MetaAdapterFactory.getConcept(conceptId, name)); myMetaInfoProvider.setConceptName(conceptId, name); myMetaInfoProvider.setKind(conceptId, myActualConcept.getKind()); myMetaInfoProvider.setScope(conceptId, myActualConcept.getScope()); if (stub != null) { // XXX here we imply stub concepts live in the save language as their origin final SConceptId stubId = myIdEncoder.parseConceptId(myActualLang.getLanguageId(), stub); myActualConcept.setStubCounterpart(stubId); myMetaInfoProvider.setStubConcept(conceptId, stubId); } } public void property(String id, String name, String index) { assert myActualConcept != null; SPropertyId propertyId = myIdEncoder.parsePropertyId(myActualConcept.getConceptId(), id); myActualConcept.addProperty(propertyId, name); myProperties.put(index, MetaAdapterFactory.getProperty(propertyId, name)); myMetaInfoProvider.setPropertyName(propertyId, name); } public void association(String id, String name, String index) { assert myActualConcept != null; SReferenceLinkId linkId = myIdEncoder.parseAssociation(myActualConcept.getConceptId(), id); myActualConcept.addLink(linkId, name); myAssociations.put(index, MetaAdapterFactory.getReferenceLink(linkId, name)); myMetaInfoProvider.setAssociationName(linkId, name); } public void aggregation(String id, String name, String index, boolean unordered) { assert myActualConcept != null; SContainmentLinkId linkId = myIdEncoder.parseAggregation(myActualConcept.getConceptId(), id); myActualConcept.addLink(linkId, name, unordered); myAggregations.put(index, MetaAdapterFactory.getContainmentLink(linkId, name)); myMetaInfoProvider.setAggregationName(linkId, name); myMetaInfoProvider.setUnordered(linkId, unordered); } // Query. De-serialize ids, resolve indexes and retrieve meta-objects according to myInfoCollector state public SConcept readConcept(@NotNull String index) { assert myConcepts.containsKey(index); return myConcepts.get(index); } public SProperty readProperty(@NotNull String index) { assert myProperties.containsKey(index); return myProperties.get(index); } public SReferenceLink readAssociation(@NotNull String index) { assert myAssociations.containsKey(index); return myAssociations.get(index); } // nullable for root nodes; to minimize code in the sax reader check is done here public SContainmentLink readAggregation(@Nullable String index) { if (index == null) { return null; } assert myAggregations.containsKey(index); return myAggregations.get(index); } public boolean isInterface(@NotNull SConcept concept) { return ConceptKind.INTERFACE == myMetaRegistry.find(concept).getKind(); } public boolean isImplementation(@NotNull SConcept concept) { return myMetaRegistry.find(concept).isImplementation(); } public boolean isImplementationWithStub(@NotNull SConcept concept) { return myMetaRegistry.find(concept).isImplementationWithStub(); } /** * This method shall be invoked only if {@link #isImplementationWithStub(org.jetbrains.mps.openapi.language.SConcept)} == <code>true</code> */ @NotNull public SConcept getStubConcept(@NotNull SConcept original) { final ConceptInfo ci = myMetaRegistry.find(original); assert ci.getKind() == ConceptKind.IMPLEMENTATION_WITH_STUB; final SConceptId stub = ci.getStubCounterpart(); assert stub != null; return MetaAdapterFactory.getConcept(stub, ci.constructStubConceptName()); } public SLanguage getLanguage(@NotNull SLanguageId langId, @NotNull String langName) { // used languages is a subset of languages detected for meta-registry, don't want to use // set of languages available from myInfoCollector, which might not be yet ready, unless we ensure // proper read order (first registry, then used languages). It's even more complicated for per-root // persistence, where usedLanguages are kept in a header file only, while registry spans few. return MetaAdapterFactory.getLanguage(langId, langName); } }