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