/* * 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.impl; import jetbrains.mps.smodel.SNodeUtil; 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.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 org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SNodeReference; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; public class CompiledConceptDescriptor extends BaseConceptDescriptor { private final SConceptId myId; private final String myConceptFqName; @Nullable private final SConceptId mySuperConceptId; private final String mySuperConcept; private final boolean myInterfaceConcept; private final List<SConceptId> myParents; private final PropertyDescriptor[] myOwnProperties; private final ReferenceDescriptor[] myOwnReferences; private final LinkDescriptor[] myOwnLinks; private final boolean myAbstract; private final boolean myFinal; private final boolean myIsRootable; private final String myConceptAlias; private final String myConceptShortDescription; private final String myHelpUrl; private final StaticScope myStaticScope; private final SNodeReference mySourceNodeRef; private final Object myLock = ""; // to be initialized private Set<SConceptId> ancestorsIds; private Map<SPropertyId, PropertyDescriptor> properties; private Map<SReferenceLinkId, ReferenceDescriptor> references; private Map<SContainmentLinkId, LinkDescriptor> links; private Map<String, PropertyDescriptor> propertiesByName; private Map<String, ReferenceDescriptor> referencesByName; private Map<String, LinkDescriptor> linksByName; private volatile boolean myInitialized = false; private int myVersion; CompiledConceptDescriptor( int version, @NotNull SConceptId id, @NotNull String conceptFqName, @Nullable SConceptId superConceptId, @Nullable String superConcept, boolean interfaceConcept, SConceptId[] parents, String[] parentNames, // ignored PropertyDescriptor[] ownProperties, ReferenceDescriptor[] ownReferences, LinkDescriptor[] ownLinks, boolean isAbstract, boolean isFinal, boolean isRootable, String conceptAlias, String shortDescription, String helpUrl, StaticScope staticScope, SNodeReference sourceNodeRef) { myVersion = version; myId = id; myConceptFqName = conceptFqName; mySuperConceptId = superConceptId; mySuperConcept = superConcept; myInterfaceConcept = interfaceConcept; if (!interfaceConcept && !SNodeUtil.conceptId_BaseConcept.equals(id)) { // make sure parents include superconcept (e.g. ConceptDescendantsCache.loadConcept implicitly assumes BC in concept's hierarchy) // we could have done this in ConceptDescriptorBuilder, but since we got two of them now, I decided to keep the code to enforce the invariant // of #getParentIds() here. if (superConceptId == null) { // for a !interface concept, missing superconcept implies BaseConcept superConceptId = SNodeUtil.conceptId_BaseConcept; } ArrayDeque<SConceptId> pp = new ArrayDeque<>(parents == null ? 2 : parents.length + 2); boolean parentsContainSuper = false; if (parents != null) { for (SConceptId p : parents) { pp.addLast(p); if (!parentsContainSuper && superConceptId.equals(p)) { parentsContainSuper = true; } } } if (!parentsContainSuper) { pp.addFirst(superConceptId); } myParents = Arrays.asList(pp.toArray(new SConceptId[pp.size()])); } else { assert superConceptId == null; myParents = parents == null || parents.length == 0 ? Collections.emptyList() : Arrays.asList(parents); } myOwnProperties = ownProperties; myOwnReferences = ownReferences; myOwnLinks = ownLinks; myAbstract = isAbstract; myFinal = isFinal; myIsRootable = isRootable; myConceptAlias = conceptAlias == null ? "" : conceptAlias; // short description and helpUrl are part of ConceptPresentation aspect, empty string is just to fulfil CD's contract myConceptShortDescription = shortDescription == null ? "" : shortDescription; myHelpUrl = helpUrl == null ? "" : helpUrl; myStaticScope = staticScope; // todo: common with StructureAspectInterpreted to new class! mySourceNodeRef = sourceNodeRef; } private void init() { if (myInitialized) { return; } synchronized (myLock) { if (myInitialized) { return; } List<ConceptDescriptor> parentDescriptors = new ArrayList<ConceptDescriptor>(myParents.size()); for (SConceptId parent : myParents) { ConceptDescriptor descriptor = ConceptRegistry.getInstance().getConceptDescriptor(parent); parentDescriptors.add(descriptor); } if (isInterfaceConcept()) { // for regular ConceptDescriptor, BaseConcept would come through superConcept hierarchy, either directly from myParents or // indirectly as (grand-)parent of a superconcept. // for interface ConceptDescriptor, we need to respect BaseConcept's features but don't need to expose it in parents (why, btw?) // At least SInterfaceConceptAdapter.getSuperInterfaces doesn't expect anything but interface CDs from getParents() parentDescriptors.add(ConceptRegistry.getInstance().getConceptDescriptor(SNodeUtil.conceptId_BaseConcept)); } initAncestors(parentDescriptors); initPropertyNames(parentDescriptors); initReferenceNames(parentDescriptors); initChildNames(parentDescriptors); myInitialized = true; } } private void initAncestors(List<ConceptDescriptor> parentDescriptors) { assert !myInitialized; ancestorsIds = new LinkedHashSet<SConceptId>(); ancestorsIds.addAll(myParents); ancestorsIds.add(myId); for (ConceptDescriptor parentDescriptor : parentDescriptors) { ancestorsIds.addAll(parentDescriptor.getAncestorsIds()); } } private void initPropertyNames(List<ConceptDescriptor> parentDescriptors) { assert !myInitialized; Map<SPropertyId, PropertyDescriptor> propsMap = new LinkedHashMap<SPropertyId, PropertyDescriptor>(); Map<String, PropertyDescriptor> propByNameMap = new LinkedHashMap<String, PropertyDescriptor>(); for (PropertyDescriptor p : myOwnProperties) { propsMap.put(p.getId(), p); propByNameMap.put(p.getName(), p); } for (ConceptDescriptor parentDescriptor : parentDescriptors) { for (PropertyDescriptor pd : parentDescriptor.getPropertyDescriptors()) { propsMap.put(pd.getId(), pd); propByNameMap.put(pd.getName(), pd); } } properties = Collections.unmodifiableMap(propsMap); propertiesByName = Collections.unmodifiableMap(propByNameMap); } private void initReferenceNames(List<ConceptDescriptor> parentDescriptors) { assert !myInitialized; Map<SReferenceLinkId, ReferenceDescriptor> refsMap = new LinkedHashMap<SReferenceLinkId, ReferenceDescriptor>(); HashMap<String, ReferenceDescriptor> refsByNameMap = new LinkedHashMap<String, ReferenceDescriptor>(); for (ReferenceDescriptor r : myOwnReferences) { refsMap.put(r.getId(), r); refsByNameMap.put(r.getName(), r); } for (ConceptDescriptor parentDescriptor : parentDescriptors) { for (ReferenceDescriptor rd : parentDescriptor.getReferenceDescriptors()) { refsMap.put(rd.getId(), rd); refsByNameMap.put(rd.getName(), rd); } } references = Collections.unmodifiableMap(refsMap); referencesByName = Collections.unmodifiableMap(refsByNameMap); } private void initChildNames(List<ConceptDescriptor> parentDescriptors) { assert !myInitialized; //ids Map<SContainmentLinkId, LinkDescriptor> linksMap = new LinkedHashMap<SContainmentLinkId, LinkDescriptor>(); HashMap<String, LinkDescriptor> linksByNameMap = new LinkedHashMap<String, LinkDescriptor>(); for (LinkDescriptor r : myOwnLinks) { linksMap.put(r.getId(), r); linksByNameMap.put(r.getName(), r); } for (ConceptDescriptor parentDescriptor : parentDescriptors) { for (LinkDescriptor ld : parentDescriptor.getLinkDescriptors()) { linksMap.put(ld.getId(), ld); linksByNameMap.put(ld.getName(), ld); } } links = Collections.unmodifiableMap(linksMap); linksByName = Collections.unmodifiableMap(linksByNameMap); } /** * This method is for internal use only. * It allows to identify whether some properties, which were added in later versions of MPS, were specified * on construction (by generated code) or they have default values. * This is needed not to make wasSet/wasNotSet field for each method. */ public int getVersion() { return myVersion; } @Override public String getConceptFqName() { return myConceptFqName; } @Override public String getSuperConcept() { return mySuperConcept; } @Override public boolean isInterfaceConcept() { return myInterfaceConcept; } @Override public StaticScope getStaticScope() { return myStaticScope; } @Override public boolean isAbstract() { return myAbstract; } @Override public boolean isRootable() { return myIsRootable; } @Override public boolean isFinal() { return myFinal; } @NotNull @Override public String getConceptAlias() { return myConceptAlias; } @Override public String getConceptShortDescription() { return myConceptShortDescription; } @Override public String getHelpUrl() { return myHelpUrl; } @Nullable @Override public SNodeReference getSourceNode() { return mySourceNodeRef; } @NotNull @Override public SConceptId getId() { return myId; } @Nullable @Override public SConceptId getSuperConceptId() { return mySuperConceptId; } @Override public List<SConceptId> getParentsIds() { return myParents; } @Override public Set<SConceptId> getAncestorsIds() { init(); return ancestorsIds; } @Override public Set<SPropertyId> getPropertyIds() { init(); return properties.keySet(); } @Override public Collection<PropertyDescriptor> getPropertyDescriptors() { init(); return properties.values(); } @Override public PropertyDescriptor getPropertyDescriptor(SPropertyId id) { init(); return properties.get(id); } @Override public PropertyDescriptor getPropertyDescriptor(String name) { init(); return propertiesByName.get(name); } @Override public Set<SReferenceLinkId> getReferenceIds() { init(); return references.keySet(); } @Override public Collection<ReferenceDescriptor> getReferenceDescriptors() { init(); return references.values(); } @Override public ReferenceDescriptor getRefDescriptor(SReferenceLinkId id) { init(); return references.get(id); } @Override public ReferenceDescriptor getRefDescriptor(String name) { init(); return referencesByName.get(name); } @Override public Set<SContainmentLinkId> getLinkIds() { init(); return links.keySet(); } @Override public Collection<LinkDescriptor> getLinkDescriptors() { init(); return links.values(); } @Override public LinkDescriptor getLinkDescriptor(SContainmentLinkId id) { init(); return links.get(id); } @Override public LinkDescriptor getLinkDescriptor(String name) { init(); return linksByName.get(name); } }