/* * Copyright 2003-2016 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.runtime.interpreted; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SPropertyOperations; import jetbrains.mps.smodel.NodeReadAccessCasterInEditor; import jetbrains.mps.smodel.SNodeUtil; import jetbrains.mps.smodel.adapter.ids.MetaIdByDeclaration; 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.language.ConceptRegistry; import jetbrains.mps.smodel.runtime.BaseLinkDescriptor; import jetbrains.mps.smodel.runtime.BasePropertyDescriptor; import jetbrains.mps.smodel.runtime.BaseReferenceDescriptor; import jetbrains.mps.smodel.runtime.ConceptDescriptor; import jetbrains.mps.smodel.runtime.LinkDescriptor; import jetbrains.mps.smodel.runtime.PropertyDescriptor; import jetbrains.mps.smodel.runtime.ReferenceDescriptor; import jetbrains.mps.smodel.runtime.StaticScope; import jetbrains.mps.smodel.runtime.base.BaseConceptDescriptor; import jetbrains.mps.smodel.runtime.illegal.IllegalConceptDescriptor; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.model.SNodeReference; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; class InterpretedConceptDescriptor extends BaseConceptDescriptor { private final static Logger LOG = LogManager.getLogger(InterpretedConceptDescriptor.class); private final String myQualifiedName; private final SConceptId myId; private boolean isInterface; private String superConcept; private SConceptId superConceptId; private List<String> parents; private List<SConceptId> parentsIds; private Set<SConceptId> ancestorsIds; private Map<SPropertyId, PropertyDescriptor> myProperties; private Map<String, PropertyDescriptor> myPropertiesByName; private Map<SReferenceLinkId, ReferenceDescriptor> myReferences; private Map<String, ReferenceDescriptor> myReferencesByName; private Map<SContainmentLinkId, LinkDescriptor> myLinks; private Map<String, LinkDescriptor> myLinksByName; private boolean isAbstract; private boolean isFinal; private boolean myIsRootable; private String conceptAlias; private String shortDescription; private String helpURL; private StaticScope staticScope; private volatile boolean myIsInitialized = false; // temp collections for delayed initialization private Map<SPropertyId, PropertyDescriptor> directPropertiesByIds = new HashMap<SPropertyId, PropertyDescriptor>(); private Map<SReferenceLinkId, ReferenceDescriptor> directReferencesByIds = new HashMap<SReferenceLinkId, ReferenceDescriptor>(); private Map<SContainmentLinkId, LinkDescriptor> directLinksByIds = new HashMap<SContainmentLinkId, LinkDescriptor>(); private Map<String, PropertyDescriptor> directPropertiesByName = new HashMap<String, PropertyDescriptor>(); private Map<String, ReferenceDescriptor> directReferencesByName = new HashMap<String, ReferenceDescriptor>(); private Map<String, LinkDescriptor> directLinksByName = new HashMap<String, LinkDescriptor>(); private SNodeReference mySourceNodeRef; InterpretedConceptDescriptor(final SNode declaration, @NotNull SConceptId id, @NotNull final String qualifiedName) { myId = id; myQualifiedName = qualifiedName; NodeReadAccessCasterInEditor.runReadTransparentAction(new Runnable() { @Override public void run() { // isInterface isInterface = declaration.getConcept().equals(SNodeUtil.concept_InterfaceConceptDeclaration); // We use declaration.getProperty directly, instead of SPropertyOperations and SNodeAccessUtil+constraints // (a) we know our core.structure language doesn't define any constraints for these properties // (b) they would require compiled code which we don't support for interpreted languages (IL being primary users of this class, ICD). // Otherwise, if we use SPropertyOperations+SNodeAccessUtil, ConstraintsDescriptors come into play, // and might query concept descriptor we are trying to initialize right now, which is not what we would like to encounter. isFinal = SPropertyOperations.getBoolean(declaration.getProperty(SNodeUtil.property_AbstractConceptDeclaration_final)); isAbstract = SPropertyOperations.getBoolean(declaration.getProperty(SNodeUtil.property_AbstractConceptDeclaration_abstract)); myIsRootable = SPropertyOperations.getBoolean(declaration.getProperty(SNodeUtil.property_Concept_Rootable)); helpURL = declaration.getProperty(SNodeUtil.property_AbstractConceptDeclaration_helpURL); if (helpURL == null) { helpURL = ""; } conceptAlias = declaration.getProperty(SNodeUtil.property_AbstractConceptDeclaration_conceptAlias); if (conceptAlias == null) { conceptAlias = ""; } shortDescription = declaration.getProperty(SNodeUtil.property_AbstractConceptDeclaration_conceptShortDescription); if (shortDescription == null) { shortDescription = ""; } // scope if (isInterface) { staticScope = StaticScope.GLOBAL; } else { String scopeVal = declaration.getProperty(SNodeUtil.property_ConceptDeclaration_staticScope); staticScope = "none".equals(scopeVal) ? StaticScope.NONE : ("root".equals(scopeVal) ? StaticScope.ROOT : StaticScope.GLOBAL); } // parents Set<String> parentsSet = new LinkedHashSet<String>(); Set<SConceptId> parentsIdsSet = new LinkedHashSet<SConceptId>(); if (declaration.getConcept().equals(SNodeUtil.concept_ConceptDeclaration)) { // super-concept SNode superConceptNode = declaration.getReferenceTarget(SNodeUtil.link_ConceptDeclaration_extends); if (superConceptNode == null && !SNodeUtil.concept_BaseConcept.getName().equals(myQualifiedName)) { superConcept = SNodeUtil.concept_BaseConcept.getName(); superConceptId = SNodeUtil.conceptId_BaseConcept; } else { superConcept = StructureAspectInterpreted.conceptFQName(superConceptNode); superConceptId = superConceptNode == null ? null : MetaIdByDeclaration.getConceptId(superConceptNode); } parentsSet.add(superConcept); parentsIdsSet.add(superConceptId); for (SNode/*<InterfaceConceptReference>*/ implementsLink : declaration.getChildren(SNodeUtil.link_ConceptDeclaration_implements)) { SNode interfaceConcept = implementsLink.getReferenceTarget(SNodeUtil.link_InterfaceConceptReference_intfc); if (interfaceConcept == null) { LOG.error("Interface concept (implements link) is null, declaration: " + declaration); continue; } parentsSet.add(StructureAspectInterpreted.conceptFQName(interfaceConcept)); parentsIdsSet.add(MetaIdByDeclaration.getConceptId(interfaceConcept)); } } else if (isInterface) { for (SNode/*<InterfaceConceptReference>*/ extendsLink : declaration.getChildren(SNodeUtil.link_InterfaceConceptDeclaration_extends)) { SNode interfaceConcept = extendsLink.getReferenceTarget(SNodeUtil.link_InterfaceConceptReference_intfc); if (interfaceConcept == null) { LOG.error("Interface concept (extends link) is null, declaration: " + declaration); continue; } parentsSet.add(StructureAspectInterpreted.conceptFQName(interfaceConcept)); parentsIdsSet.add(MetaIdByDeclaration.getConceptId(interfaceConcept)); } } parentsSet.remove(null); parentsIdsSet.remove(null); if (superConcept == null && !SNodeUtil.concept_BaseConcept.getName().equals(myQualifiedName)) { parentsSet.add(SNodeUtil.concept_BaseConcept.getName()); parentsIdsSet.add(SNodeUtil.conceptId_BaseConcept); } parents = new ArrayList<String>(parentsSet); parentsIds = new ArrayList<SConceptId>(parentsIdsSet); // direct properties for (SNode property : declaration.getChildren(SNodeUtil.link_AbstractConceptDeclaration_propertyDeclaration)) { String name = property.getProperty(SNodeUtil.property_INamedConcept_name); if (name != null) { SPropertyId propId = MetaIdByDeclaration.getPropId(property); BasePropertyDescriptor pd = new BasePropertyDescriptor(propId, name); directPropertiesByIds.put(propId, pd); directPropertiesByName.put(name, pd); } } // direct references and children for (SNode link : declaration.getChildren(SNodeUtil.link_AbstractConceptDeclaration_linkDeclaration)) { // process link declarations, excluding those that specialize some other link. // We don't generate anything for such links, thus exclude them here as well. if (link.getReference(SNodeUtil.link_LinkDeclaration_specializedLink) != null) { continue; } String role = link.getProperty(SNodeUtil.property_LinkDeclaration_role); if (role == null) { continue; } boolean unordered = SPropertyOperations.getBoolean(link.getProperty(SNodeUtil.property_LinkDeclaration_unordered)); SNode linkTargetConcept = link.getReferenceTarget(SNodeUtil.link_LinkDeclaration_target); final SConceptId targetConceptId = linkTargetConcept == null ? SNodeUtil.conceptId_BaseConcept : MetaIdByDeclaration.getConceptId(linkTargetConcept); final String linkCardinality = link.getProperty(SNodeUtil.property_LinkDeclaration_sourceCardinality); final boolean isOptional = !SNodeUtil.isAtLeastOne(linkCardinality); if (SNodeUtil.isAssociationLink(link.getProperty(SNodeUtil.property_LinkDeclaration_metaClass))) { SReferenceLinkId refId = MetaIdByDeclaration.getRefRoleId(link); BaseReferenceDescriptor pd = new BaseReferenceDescriptor(refId, role, targetConceptId, isOptional); directReferencesByIds.put(refId, pd); directReferencesByName.put(role, pd); } else { final boolean isMultiple = !SNodeUtil.isAtMostOne(linkCardinality); SContainmentLinkId linkId = MetaIdByDeclaration.getLinkId(link); BaseLinkDescriptor pd = new BaseLinkDescriptor(linkId, role, targetConceptId, isOptional, isMultiple, unordered); directLinksByIds.put(linkId, pd); directLinksByName.put(role, pd); } } mySourceNodeRef = declaration.getReference(); } }); } private void init() { if (myIsInitialized) { return; } synchronized (this) { if (myIsInitialized) { return; } // get parent descriptors List<ConceptDescriptor> parentDescriptors = new ArrayList<ConceptDescriptor>(parents.size()); for (SConceptId parent : parentsIds) { ConceptDescriptor descriptor = ConceptRegistry.getInstance().getConceptDescriptor(parent); if (!(descriptor instanceof IllegalConceptDescriptor)) { parentDescriptors.add(descriptor); } } // ancestors ancestorsIds = new HashSet<SConceptId>(parentsIds); ancestorsIds.add(myId); for (ConceptDescriptor parentDescriptor : parentDescriptors) { ancestorsIds.addAll(parentDescriptor.getAncestorsIds()); } // properties Map<SPropertyId, PropertyDescriptor> propertiesByIds = new LinkedHashMap<SPropertyId, PropertyDescriptor>(); Map<String, PropertyDescriptor> propertiesByName = new LinkedHashMap<String, PropertyDescriptor>(); propertiesByIds.putAll(directPropertiesByIds); propertiesByName.putAll(directPropertiesByName); for (ConceptDescriptor parentDescriptor : parentDescriptors) { for (PropertyDescriptor pd : parentDescriptor.getPropertyDescriptors()) { propertiesByIds.put(pd.getId(), pd); propertiesByName.put(pd.getName(), pd); } } myProperties = Collections.unmodifiableMap(propertiesByIds); myPropertiesByName = Collections.unmodifiableMap(propertiesByName); // references Map<SReferenceLinkId, ReferenceDescriptor> referencesByIds = new LinkedHashMap<SReferenceLinkId, ReferenceDescriptor>(); Map<String, ReferenceDescriptor> referencesByName = new LinkedHashMap<String, ReferenceDescriptor>(); referencesByIds.putAll(directReferencesByIds); referencesByName.putAll(directReferencesByName); for (ConceptDescriptor parentDescriptor : parentDescriptors) { for (ReferenceDescriptor rd : parentDescriptor.getReferenceDescriptors()) { referencesByIds.put(rd.getId(), rd); referencesByName.put(rd.getName(), rd); } } myReferences = Collections.unmodifiableMap(referencesByIds); myReferencesByName = Collections.unmodifiableMap(referencesByName); // children Map<SContainmentLinkId, LinkDescriptor> linksByIds = new LinkedHashMap<SContainmentLinkId, LinkDescriptor>(); Map<String, LinkDescriptor> linksByName = new LinkedHashMap<String, LinkDescriptor>(); linksByIds.putAll(directLinksByIds); linksByName.putAll(directLinksByName); for (ConceptDescriptor parentDescriptor : parentDescriptors) { for (LinkDescriptor ld : parentDescriptor.getLinkDescriptors()) { linksByIds.put(ld.getId(), ld); linksByName.put(ld.getName(), ld); } } myLinks = Collections.unmodifiableMap(linksByIds); myLinksByName = Collections.unmodifiableMap(linksByName); directPropertiesByIds = null; directReferencesByIds = null; directLinksByIds = null; directPropertiesByName = null; directReferencesByName = null; directLinksByName = null; myIsInitialized = true; } } @Override public String getConceptFqName() { return myQualifiedName; } @Override public String getSuperConcept() { return superConcept; } @Override public boolean isInterfaceConcept() { return isInterface; } @Override public boolean isRootable() { return myIsRootable; } @Override public StaticScope getStaticScope() { return staticScope; } @Override public boolean isAbstract() { return isAbstract; } @Override public boolean isFinal() { return isFinal; } @NotNull @Override public String getConceptAlias() { return conceptAlias; } @Override public String getConceptShortDescription() { return shortDescription; } @Override public String getHelpUrl() { return helpURL; } @Nullable @Override public SNodeReference getSourceNode() { return mySourceNodeRef; } @NotNull @Override public SConceptId getId() { return myId; } @Nullable @Override public SConceptId getSuperConceptId() { return superConceptId; } @Override public List<SConceptId> getParentsIds() { init(); return parentsIds; } @Override public Set<SConceptId> getAncestorsIds() { init(); return ancestorsIds; } @Override public Set<SPropertyId> getPropertyIds() { init(); return myProperties.keySet(); } @Override public Collection<PropertyDescriptor> getPropertyDescriptors() { init(); return myProperties.values(); } @Override public PropertyDescriptor getPropertyDescriptor(SPropertyId id) { init(); return myProperties.get(id); } @Override public PropertyDescriptor getPropertyDescriptor(String name) { init(); return myPropertiesByName.get(name); } @Override public ReferenceDescriptor getRefDescriptor(SReferenceLinkId id) { init(); return myReferences.get(id); } @Override public ReferenceDescriptor getRefDescriptor(String name) { init(); return myReferencesByName.get(name); } @Override public Set<SContainmentLinkId> getLinkIds() { init(); return myLinks.keySet(); } @Override public Collection<LinkDescriptor> getLinkDescriptors() { init(); return myLinks.values(); } @Override public Set<SReferenceLinkId> getReferenceIds() { init(); return myReferences.keySet(); } @Override public Collection<ReferenceDescriptor> getReferenceDescriptors() { init(); return myReferences.values(); } @Override public LinkDescriptor getLinkDescriptor(SContainmentLinkId id) { init(); return myLinks.get(id); } @Override public LinkDescriptor getLinkDescriptor(String name) { init(); return myLinksByName.get(name); } }