/*
* 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;
}
}