/* * Copyright 2003-2014 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.smodel.adapter.ids.MetaIdHelper; import jetbrains.mps.smodel.adapter.ids.SConceptId; import jetbrains.mps.smodel.adapter.ids.SContainmentLinkId; import jetbrains.mps.smodel.adapter.ids.SPropertyId; import jetbrains.mps.smodel.adapter.ids.SReferenceLinkId; import jetbrains.mps.smodel.runtime.ConceptKind; import jetbrains.mps.smodel.runtime.StaticScope; import jetbrains.mps.util.NameUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.mps.openapi.language.SConcept; import org.jetbrains.mps.openapi.language.SContainmentLink; import org.jetbrains.mps.openapi.language.SProperty; import org.jetbrains.mps.openapi.language.SReferenceLink; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.model.SNodeUtil; import org.jetbrains.mps.openapi.model.SReference; /** * Populate a data structure that keeps meta-information actually used for the nodes supplied. * Cares about meta-model source alternatives, and consults {@link jetbrains.mps.persistence.MetaModelInfoProvider} for actual * meta-model information necessary for current persistence session. * * XXX in fact, ConceptInfo and other are pretty much what we keep in MetaModelInfoProvider. Perhaps, better code would emerge * if there is hierarchy of ConceptInfo, PropertyInfo plus two distinct mechanism to fill it: one for regular use, with ConceptDescriptors and alike, * and second for ant/merge, which populates exactly same structure but from information extracted from other models. */ public class IdInfoCollector { private final IdInfoRegistry myRegistry; private final MetaModelInfoProvider myMetaInfoProvider; public IdInfoCollector(@NotNull IdInfoRegistry registry, @NotNull MetaModelInfoProvider metaInfoProvider) { myMetaInfoProvider = metaInfoProvider; myRegistry = registry; } //////////////////////// public void fill(Iterable<SNode> nodes) { for (SNode n1 : nodes) { fillConcept(n1); fillProperties(n1); fillAssociations(n1); if (n1.getParent() != null) { fillAggregation(n1); } for (SNode n2 : SNodeUtil.getDescendants(n1, null, false)) { fillConcept(n2); fillProperties(n2); fillAssociations(n2); fillAggregation(n2); } } myRegistry.initializeIndexValues(new IdEncoder()); } //////////////// private void fillConcept(SNode n) { final SConcept concept = n.getConcept(); SConceptId conceptId = MetaIdHelper.getConcept(concept); assert conceptId != null : String.format("Can't get identity of concept %s of node %s", concept, n.getReference()); registerConcept(conceptId); } private void fillProperties(SNode n) { for (SProperty prop : n.getProperties()) { SPropertyId propId = MetaIdHelper.getProperty(prop); assert propId != null : String.format("Can't get identity of property %s of node %s", prop, n.getReference()); SConceptId conceptId = propId.getConceptId(); final ConceptInfo conceptInfo = registerConcept(conceptId); if (!conceptInfo.knows(propId)) { conceptInfo.addProperty(propId, myMetaInfoProvider.getPropertyName(propId)); } } } private void fillAssociations(SNode n) { for (SReference ref : n.getReferences()) { final SReferenceLink l = ref.getLink(); SReferenceLinkId linkId = MetaIdHelper.getAssociation(l); assert linkId != null : String.format("Can't get identity of association %s of node %s", l, n.getReference()); SConceptId conceptId = linkId.getConceptId(); final ConceptInfo conceptInfo = registerConcept(conceptId); if (!conceptInfo.knows(linkId)) { conceptInfo.addLink(linkId, myMetaInfoProvider.getAssociationName(linkId)); } } } // unlike association, records link to parent node private void fillAggregation(SNode n) { final SContainmentLink l = n.getContainmentLink(); SContainmentLinkId linkId = MetaIdHelper.getAggregation(l); assert linkId != null : String.format("Can't get identity of aggregation %s of node %s", l, n.getReference()); SConceptId conceptId = linkId.getConceptId(); final ConceptInfo conceptInfo = registerConcept(conceptId); if (!conceptInfo.knows(linkId)) { conceptInfo.addLink(linkId, myMetaInfoProvider.getAggregationName(linkId), myMetaInfoProvider.isUnordered(linkId)); } } /** * find info object for already registered concept, or register both concept and its language if it's the first time we see the concept. */ @NotNull private ConceptInfo registerConcept(SConceptId concept) { if (myRegistry.knows(concept)) { return myRegistry.get(concept); } // this is the first time we encounter the concept, it's the only time then we add it with its language if (!myRegistry.knows(concept.getLanguageId())) { String langName = myMetaInfoProvider.getLanguageName(concept.getLanguageId()); if (langName == null || langName.isEmpty()) { // next is alternative implementation of workaround introduced in rev a8f4eb1 as // MetaAdapterFactory.getConcept(concept, conceptName).getLanguage().getQualifiedName() langName = NameUtil.namespaceFromConceptFQName(myMetaInfoProvider.getConceptName(concept)); } myRegistry.registerLanguage(concept.getLanguageId(), langName); } String conceptName = myMetaInfoProvider.getConceptName(concept); ConceptInfo ci = myRegistry.registerConcept(concept, conceptName); final StaticScope scope = myMetaInfoProvider.getScope(concept); final ConceptKind kind = myMetaInfoProvider.getKind(concept); ci.setImplementationKind(scope, kind); if (kind == ConceptKind.IMPLEMENTATION_WITH_STUB) { ci.setStubCounterpart(myMetaInfoProvider.getStubConcept(concept)); } return ci; } }